Sign in to follow this  

Constructors, Factory Methods and Destructor Questions

This topic is 1108 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Is it always necessary to provide the default constructor (zero arguments) for your classes? I've been making it a point to provide a default constructor to my classes for the last couple years. 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. 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.

 

Then, there are destructors... Is it good practice to always have virtual destructors? I always do this in case I want to extend that class. Since C++ doesn't allow us to declare a class as "sealed" or "final", I'd just always declare them as virtual so that they're always called.

 

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?

Share this post


Link to post
Share on other sites

Hey guys,

 

I had a lengthy response going yesterday, but I ran out of time to finish it. Then, I lost the post. I'll try to rewrite it later.

 

I'd like to mention is that it sounds like inheritance should be used sparingly in C++ whereas other languages like Objective-C and C# thrive off of it. Objective-C requires everything you make to inherit from something else that all derives down to NSObject, in fact. I see the points in presented protected constructors and destructors for cases regarding factories and templates make sense.

 

@Servant of the Lord: You bring up many good points, and thank you for pointing out C++-11's final keyword. I forgot about that. You also bring up good points about trusting others to be reasonably responsible with code written by others. I mean, if OpenGL gets incorrect information, you'll either get a crash or undefined/unexpected results after all.

 

@Hodgman: The idea of having leaner constructors make quite a bit of sense. I usually try to do lots of initial setting stuff in my constructors, and file loading. I should probably move the file loading to a factory.

 

@dmatter: You also bring up many good points, and reinforce what others have said in this post. It's sinking in for me now lol. You also bring up interesting stuff about protected destructors that I'd like to ask you more about when I have time to test out what you've said (and think it through --lots to have sink in there for me haha).

Share this post


Link to post
Share on other sites

I'd like to mention is that it sounds like inheritance should be used sparingly in C++ whereas other languages like Objective-C and C# thrive off of it.

 

I wouldn't say "sparingly". Inheritance is used alot, but it's just not the first tool you reach for.

 

In C++, you want to prefer composition over inheritance. But inheritance isn't avoided, it's just another tool in your toolbox when you need something more.

It depends on your program's overall architecture, though. In some C++ libraries, for certain purposes, it's good to make alot of objects inherit a single base class.

 

A common use of this is with GUI widget libraries - because you often want to write run-time* generic "for every child widget" code, since many 'widgets' contain children that also are widgets but without knowing what kind of widgets the children are.

 

*If you want compile-time generic functionality, you use templates - another very powerful tool.

 

C++ has a design goal: "Don't pay for what you don't need", that makes the language lightning fast. So when I say virtual inheritance "costs" extra, I don't want to give the wrong impression that virtual inheritance is slow. It's not. It's still lightning fast, but slightly less lightning fast than not using virtual. Other languages often opt-in to pay the same costs 100% of the time, whereas C++ says, "Only pay for it when you actually want it".

 

This influences alot of how the language is designed, and how the language is used.

Share this post


Link to post
Share on other sites

Some examples for the original questions; I have these two helper classes:


class NonCopyable
{
public:
NonCopyable(){}
private:
NonCopyable( const NonCopyable& );
NonCopyable& operator=( const NonCopyable& );
};

class NoCreate
{
private:
NoCreate(){}
NoCreate( const NoCreate& );
NoCreate& operator=( const NoCreate& );
};
I use NonCopyable when I don't have a need for a class to be able to make copies of itself and/or when implementing copying would be too complex. I'm a big fan of YAGNI and KISS, so if I don't need a copy operator, I don't write it.
class SomeComplexThing : NonCopyable
{
public:
SomeComplexThing(const char* filenameToOperateOn);
~SomeComplexThing();
};
Without inheriting from NonCopyable, the above class would be in breach of the rule of three. Inheriting NonCopyable satisfies the rule, while not requiring me to actually implement copying. If a user tries to copy an object of this class, they'll get a compile time error saying it's not allowed.
 
NoCreate is a lot more esoteric. I use it as a helper for a type of non-polymorphic interfaces, something similar to the PIMPL pattern.
//header
class Doodad : NoCreate
{
public:
int GetHealth();
std::string GetName();
};
//user cannot create/destroy Doodads, only get pointers to the ones owned by the complex thing
class SomeComplexThing : NonCopyable
{
public:
SomeComplexThing(const char* filename);
int GetDoodadCount() const;
Doodad* GetDoodadByIndex(int i) const;
private:
void* buffer;
};

//cpp file
SomeComplexThing::SomeComplexThing(const char* fn) : bufffer(LoadFile(fn)) {}
SomeComplexThing::~SomeComplexThing() { FreeFile(buffer); }

struct DoodadFile //implementation of the Doodad interface
{
int count;
struct Item
{
int health;
char name[64];
} items[];
};

int SomeComplexThing::GetDoodadCount() const
{
DoodadFile* d = (DoodadFile*)buffer;
return d->count;
}
Doodad* SomeComplexThing::GetDoodadByIndex(int i) const
{
DoodadFile* d = (DoodadFile*)buffer;
if( i >=0 && i < d->count )
return (Doodad*)&d->items[i];
return 0;
}

int Doodad::GetHealth()
{
DoodadFile::Item* self = (DoodadFile::Item*)this;
return self->health;
}
std::string Doodad::GetName()
{
DoodadFile::Item* self = (DoodadFile::Item*)this;
return std::string(self->name);
}

Small point of order - modern C++ lets you delete functions with "=delete". This implements "non-copyable" in a much cleaner way, and one that the compiler can catch at compile time, rather then link time.

class DoNotCopyMe
{
public:
DoNotCopyMe() = default; // Use the compiler-provided defaults
~DoNotCopyMe() = default;

DoNotCopyMe(const DoNotCopyMe&) = delete; // Compiler will error at compile time if either of these are used
DoNotCopyMe& operator=(const DoNotCopyMe&) = delete;
};
(This doesn't cover movable types, but that's a whole 'nother kettle o' fish) Edited by SmkViper

Share this post


Link to post
Share on other sites


Another downside to be aware of though, is when you use a complex constructor (and no default constructor), you're limiting your class from being usable in some places, e.g. std::vector will no longer work (but std::vector will).
*>

That's not true.  The only requirement for use in std::vector is that a type is assignable and copyable (and in modern C++, those restrictions are relaxed to simply movable), neither of which requires a default constructor.

 

What may be more likely is that if you have a class like an asset loader, it's not copyable so you can't stick it into a standard container.  That really has nothing to do with the complexity of the constructor but rather either the class invariants (eg. "there can be only one of these") or the size of its data (eg. copying is too expensive).

Share this post


Link to post
Share on other sites

 

Another downside to be aware of though, is when you use a complex constructor (and no default constructor), you're limiting your class from being usable in some places, e.g. std::vector will no longer work (but std::vector will).

*>
That's not true.  The only requirement for use in std::vector is that a type is assignable and copyable (and in modern C++, those restrictions are relaxed to simply movable), neither of which requires a default constructor.

 


Some containers do have member functions that use default-constructed values as default-parameters.

 

std::vector<MyType> myVector(50), for example. Default-constructs 50 elements. You have to call std::vector<MyType> myVector(50, <some value to copy from>) if you don't have a default constructor.

 

And if you don't have a default constructor OR a copy-constructor, then you can't call resize() (or the resizing std::vector constructors). In that case, you'll have to just call push_back(std::move(value)) or emplace_back(construction params), probably after calling reserve() first.

 

Not show-stopping, but something you have to work around. It's not very convenient when some of the convenience functions are unusable. smile.png

Share this post


Link to post
Share on other sites

Small point of order - modern C++ lets you delete functions with "=delete". This implements "non-copyable" in a much cleaner way, and one that the compiler can catch at compile time, rather then link time.

You get a compile time error with the older method as well, because the (unlinkable) functions are private.
 

What does C# have that makes composition easier than in say C++?

I wasn't saying that C# is better than C++ in this regard -- I mentioned C#'s support for composition because the OP stated that C# seems to thrive on inheritance.
...but anyway, it depends which C++ you're talking about smile.png
C++ now has first-class-functions (via the standard library, not a language feature) and lambdas, but C# had them long before they were standardized in the C++ world.
Before C++ 11, you had to either use boost::function and/or roll your own lambdas/closures using a rediculous amount of boilerplate code (usually a struct with a constructor that captures the locals explicitly, and an operator() so it acts like a functor).
C#'s generics aren't equal to C++'s templates. In a lot of ways, templates are way more powerful... but generics also have some interesting features similar to the proposed C++ 'concepts' feature. Plus C#'s compilation system is modern, letting you actually use generics without worrying about "moving code into a header file", ruining compile times, ruining inter-system coupling, etc...
Both generics and C#'s actual duck typing support can be used to reduce the amount of boilerplate 'adapter pattern' style code that's required when gluing self-contained components into larger systems.

Edited by Hodgman

Share this post


Link to post
Share on other sites

Small point of order - modern C++ lets you delete functions with "=delete". This implements "non-copyable" in a much cleaner way, and one that the compiler can catch at compile time, rather then link time.

You get a compile time error with the older method as well, because the (unlinkable) functions are private.


Unless the code calling the copy constructor/operator = is in your base class. (Not too likely with your inheritance method - more likely if you want to avoid inheritance)

It's debatable as to whether a "cannot call private method" or "cannot call deleted method" is a clearer compiler error smile.png

I personally prefer the =delete method simply because it "reads" better to me. "Hey guys, here's this function - or function overload - that I know might be called, but I want to specifically make sure it isn't"

It's also useful for when you, say, want a function to be able to accept some types of arguments but not others (even though the compiler would silently cast for you). Edited by SmkViper

Share this post


Link to post
Share on other sites

I've been scraping with it being the Thanksgiving holiday and all. Here's a little bit of a response I have so far:

 


A common use of this is with GUI widget libraries - because you often want to write run-time* generic "for every child widget" code, since many 'widgets' contain children that also are widgets but without knowing what kind of widgets the children are.
 
*If you want compile-time generic functionality, you use templates - another very powerful tool.
 
C++ has a design goal: "Don't pay for what you don't need", that makes the language lightning fast. So when I say virtual inheritance "costs" extra, I don't want to give the wrong impression that virtual inheritance is slow. It's not. It's still lightning fast, but slightly less lightning fast than not using virtual. Other languages often opt-in to pay the same costs 100% of the time, whereas C++ says, "Only pay for it when you actually want it".

I agree. I think it makes sense that there will be systems, such as the Widget system for menus, to have a base class that everything inherits from. That way, a controller class can iterate through everything, and operate on the objects generically. This type of example heavily suggests the need for virtual methods that could be overridden by sub-classes to do their specific tasks. Now, what if I wanted to build a system where everything had to inherit from something, but there was no need for virtual members? All sub-classes have to inherit from the base class that's being treated as an interface. Would that generate a vtable for each sub-class, or is that compiler-specific?

 


I would say that in every OO language, inheritance should be use sparingly, but unfortunately many people suffer from inheritance-addiction.

I'm a recovering abuser of inheritancelaugh.png I'm starting to realize that templates are a good answer to some cases. I'm under the impression that templates still maintain the performance you'd lose by creating a vtable for sub-classes at the cost of additional compilation time, and executable size. I'm not too concerned about that just yet.

Share this post


Link to post
Share on other sites

Now, what if I wanted to build a system where everything had to inherit from something, but there was no need for virtual members?

That sounds a little odd to me. You'd have to question why you're choosing to use inheritance there.

Would that generate a vtable for each sub-class, or is that compiler-specific?

If there's no virtual then there's no vtable. That's the case for any compiler I'm aware of. But, yes, you are in the hands of the compiler there.

Share this post


Link to post
Share on other sites

I agree. I think it makes sense that there will be systems, such as the Widget system for menus, to have a base class that everything inherits from. That way, a controller class can iterate through everything, and operate on the objects generically. This type of example heavily suggests the need for virtual methods that could be overridden by sub-classes to do their specific tasks. Now, what if I wanted to build a system where everything had to inherit from something, but there was no need for virtual members? All sub-classes have to inherit from the base class that's being treated as an interface. Would that generate a vtable for each sub-class, or is that compiler-specific?

I should point out that talking about Widgets, or a GUI in general is a special topic. A UI usually follows different design patterns than most other components in say, a game will follow.

Why would you want to inherit without having a vtable?

I'm a recovering abuser of inheritance:lol: I'm starting to realize that templates are a good answer to some cases. I'm under the impression that templates still maintain the performance you'd lose by creating a vtable for sub-classes at the cost of additional compilation time, and executable size. I'm not too concerned about that just yet.

In my opinion becoming an abuser of templates is much worse than an abuser of inheritance, make sure you don't start looking at templates as the magical solution to everything.

Share this post


Link to post
Share on other sites

Exactly. There is no "one tool fits every solution". Only "one tool can be abused to solve every solution badly" - whether polymorphism, macros (or other pre-processing methods), templates, callbacks (and signals/slots), or any other feature; they all have value in some circumstances, and can cause pain in others.

Games aren't made from a single paradigm. When using ECS, "normal" OOP, procedural programming, or whatever else, don't lock yourself into the mindset that your game's entire architecture needs to fit into one paradigm or pattern. Different parts of your architecture are better suited for different styles of design or different tools.

While the bulk of a game may lean more towards ECS, the rendering sub-system itself may lean in a different direction, data serialization in a different one, big picture networking between the game and the server and the other clients yet another architectural design.
Just because ECS doesn't lend itself to UI very well, I wouldn't call UI a special case.

 

There are so many different problems, sometimes even specialized languages are designed just to lend themselves better to those problem-types.
 

Now, what if I wanted to build a system where everything had to inherit from something, but there was no need for virtual members? All sub-classes have to inherit from the base class that's being treated as an interface.

The question becomes, why do all sub-classes have to inherit from a base class? What concrete objective are you aiming for?
 
Are you wanting to inherit features? Use composition.
Are you wanting to use a common interface at compile-time? Use templates.
re you wanting to use a common interface at runtime? Maybe it'd make sense for certain functionality to be handled by registering callbacks, perhaps using std::function.
 
And inheritance is the best choice in some situations, so maybe that this is one of those cases.
 
But you have to give concrete example about what you are wanting to achieve, and why you are wanting to achieve it.
 

Would that generate a vtable for each sub-class, or is that compiler-specific?

vtables themselves aren't part of the standard. They are just how pretty much every compiler decides to implement the features (virtual functions) that the standard demands be implemented.

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

This topic is 1108 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this