Is it always necessary to provide the default constructor (zero arguments) for your classes?
No it is not required to have a default constructor.
It's not necessarily good practice to always have a default constructor either. Only if your class would benefit from one. Just like any other piece of code: Only write it if you need it.
I see far too many default constructors that leave objects in a broken, uninitialised or just meaningless state; all because they think they should always add a default constructor.
There are many classes that are responsible for loading files, and I typically provide a constructor with a filename and path string for doing that. Then, the constructor becomes the loader. I've heard this is bad practice, and factory methods should be provided instead.
File loading in constructors is done by the standard file-streams themselves, so I wouldn't beat yourself up over it!
I agree with using a factory method, but then how do I get around inheritance? What if I wanted to extend my object? I wouldn't be able to extend my factory function, or as I prefer, static factory method, if I wanted to add additional functionality to the subclass without providing a wrapper static method that eventually calls the superclass' static factory method.
Inheritance is something to use sparingly in the first place. When it is used there are special design considerations to make - and that's what you're seeing here.
As a rule of thumb: If your class is intended to be a concrete (fully instantiable) implementation of something in its own right then it should not also be used as a base class. Base classes should be abstract (non-instantiable, partial implementations) or without any implementation at all which is true in the case of an interface.
With that in mind, the answer to the question "What if I wanted to extend my object?" is: Don't.
Then, there are destructors... Is it good practice to always have virtual destructors?
Nope. Only if you need polymorphic destruction. Generally if you have other virtual functions then you can reasonably expect to need polymorphic destruction too, so make the destructor virtual.
Since C++ doesn't allow us to declare a class as "sealed" or "final"
Modern C++ has a final keyword.
Other than that, you could make the constructor private and use the named-constructor idiom. Not ideal though. I would only go that far if there was any particular risk of (and danger caused by) extending a class.
Finally, what about accessors imposed on constructors and destructors? I've always just made them public. Would it ever make sense to make a constructor or destructor private or even protected? I could see value in making a constructor meant only to be used solely by the class' factory methods, and nowhere else. Then however, why would anyone ever want to reduce access to a destructor?
Well I've just mentioned making constructors private to prevent people inheriting from it.
A protected constructor is useful in the same way that regular protected functions are - they allow derived classes to access functionality that you don't want to expose publically.
Private destructors are rare (I have never once needed to write a private destructor myself) and most cases where they do exist could probably have been dealt with differently. But they get used when a class needs to handle deletion in a special way, for example a class that expects to be allocated on the heap and freed by a specially collaborating reference-counting handle. It might hide its destructor to prevent manual destruction.
Protected destructors are used for non-polymorphic base-classes: A base class that isn't intended to be used polymorphically. This would be the case if the base-class expects to be inheritted from but only to inject something into that derived class (e.g. a templated base-class comprised only of typedefs). In this case polymorphic deletion of this base-class isn't expected so a virtual destructor isn't needed either (and adding one "just to be safe" would incur the downside of a vtable) and making the destructor protected will catch the misuse at compile-time.