Sign in to follow this  
Mantear

Verifying defined behavior [C++]

Recommended Posts

Greetings, I've got a C++ code snippet that works the way I intended it to. However, I just want to verify that what it is doing is defined behavior and not just working the way I want because that's how the compiler did it. I also want to be aware of any pitfalls. Here's an example:
class Base
{
   Base() { SomeFunction(); }

   virtual void SomeFunction() { /* do 'A' */ }
};

class Derived : public Base
{
   Derived() { SomeFunction(); }

   void SomeFunction() { /* do 'B' */ }
};
In this example, the base class constructor calls SomeFunction() that performs default behavior, 'A', that most derived class will want to do. However, some derived classes will over-ride SomeFunction(). So, in the Derived class constructor, I call SomeFunction() again. This ends up calling both versions of SomeFunction(), first the Base class version then the Derived class version, which is what I want. The Base class version of SomeFunction() initializes some things to default values and the Derived class version of SomeFunction() updates those same values to be specific to the Derived class. A) Is this defined behavior? (I'm pretty sure it is) B) Is there anything wrong with what I'm doing? Thanks!

Share this post


Link to post
Share on other sites
It should work.

However...

Quote:
The Base class version of SomeFunction() initializes some things to default values and the Derived class version of SomeFunction() updates those same values to be specific to the Derived class


Aren't there things called constructors?


class Base {
public:
Base(x_ = 10, y_=20)
: x(x_)
, y(y_)
{}
protected:

int x;
private:
int y;

};

class Derived : public Base
{
public:
Derived( z_ = 200 )
: Base(17, 9999)
{}
private:
int z;
};



Using the above approach you follow proper RAII, and avoid multiple assignments to same variable.

BTW: if you use some dynamically allocated resources, calling some function multiple times is a nightmare to manage. One allocates, other de-allocates, neither knows about each other, yet tightly depends on it.

Share this post


Link to post
Share on other sites
Quote:

B) Is there anything wrong with what I'm doing?

Yes, see the above post. Why are you doing this? From the information you've provided, it's just a clunky, obscure, hard-to-maintain and rather poorly-designed way to mimic exactly what constructors do automatically. Except it doesn't do it as well.

I assume there's more to this?

Share this post


Link to post
Share on other sites
SomeFunction() calculates the default values for some of the member variables, I don't have default values that I can simply assign in the initialization list. I suppose I could put the calculations within the initlaization list, but I think that would look quite ugly. So I made an default initialization method in the Base class that does it for me. I made it a seperate method because there may be times that I want to re-initialize the values.

The Derived class neeeds to be able to initialize the variables to default values using different calculations specific to the Derived class. However, many of the other derived classes will just use the Base class version, not have their own version of SomeFunction(), and will not call it in their constructor.

Does that clear it up at all? Is there still a better way to do it?
Thanks.

Share this post


Link to post
Share on other sites
It sounds like Base::SomeFunction() and Derived::SomeFunction() do different things. In which case, why give them the same name? Of course, we'd be able to better comment if you showed actual code for what you were doing.

Share this post


Link to post
Share on other sites
Maybe I'm missing something, but that code shouldn't work (at least not how you expect it to). Calling a virtual function inside a constructor (or destructor) will always call the base version, so both constructors in your code call Base::SomeFunction().

Share this post


Link to post
Share on other sites
Quote:
SomeFunction() calculates the default values for some of the member variables, I don't have default values that I can simply assign in the initialization list. I suppose I could put the calculations within the initlaization list, but I think that would look quite ugly. So I made an default initialization method in the Base class that does it for me. I made it a seperate method because there may be times that I want to re-initialize the values.


I stand by my original suggestion.


struct Parameters
{
Parameters( int x, int y, int z ) {
// do some seriouz calculationz
}

Parameters( const Parameters &p ) {
: result1( p.result1 )
, result2( p.result2 )
}

var_t result1;
big_var_t result2;
};

class Base // Or, you can inherit Parameters
{
public:
Base( const Parameters &p = Parameters(10, 20, 30) )
: params( p )
{}
private:
Parameters params;

};

class Derived : public Base
{
Derived()
: Base( Parameters( -1, -1, 1 ) )
{}
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
Maybe I'm missing something, but that code shouldn't work (at least not how you expect it to). Calling a virtual function inside a constructor (or destructor) will always call the base version, so both constructors in your code call Base::SomeFunction().


No. If you have a base class Base, with a derived class Derived, calling a virtual function in a Base constructor will call the Base version, but calling it in a Derived constructor will call the Derived version. See section 12.7 paragraph 3 in the C++ Standard.

Share this post


Link to post
Share on other sites
Quote:

Original post by SiCrane
No. If you have a base class Base, with a derived class Derived, calling a virtual function in a Base constructor will call the Base version, but calling it in a Derived constructor will call the Derived version.


I just tested this and you're right (not that I was doubting you or anything [smile]).

But I do remember an issue regarding calling virtual functions in constructors. Any idea what it might be?

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
Quote:

Original post by SiCrane
No. If you have a base class Base, with a derived class Derived, calling a virtual function in a Base constructor will call the Base version, but calling it in a Derived constructor will call the Derived version.


I just tested this and you're right (not that I was doubting you or anything [smile]).

But I do remember an issue regarding calling virtual functions in constructors. Any idea what it might be?


The issue is exactly that the call in the Base constructor will call the Base version (even when it's being called indirectly by the construction of the Derived object).

More.

Share this post


Link to post
Share on other sites
Quote:

Original post by Zahlman
The issue is exactly that the call in the Base constructor will call the Base version (even when it's being called indirectly by the construction of the Derived object).


I understand. Thank you.

Share this post


Link to post
Share on other sites
I'd do it how Mantear does it - why do a load of extra copying and setting up of a new class (which doubles maintenance since if you add a var to base you have to add it to params too)? It's not like he's invoking undefined behaviour or anything.

Share this post


Link to post
Share on other sites
Quote:

SomeFunction() calculates the default values for some of the member variables, I don't have default values that I can simply assign in the initialization list. I suppose I could put the calculations within the initlaization list, but I think that would look quite ugly. So I made an default initialization method in the Base class that does it for me. I made it a seperate method because there may be times that I want to re-initialize the values.

The Derived class neeeds to be able to initialize the variables to default values using different calculations specific to the Derived class. However, many of the other derived classes will just use the Base class version, not have their own version of SomeFunction(), and will not call it in their constructor.

Does that clear it up at all? Is there still a better way to do it?
Thanks.

So, I would still advocate just using constructors. Up until the point where I read that yout "made it a seperate method because there may be times that I want to re-initialize the values." Okay, fair enough -- this is a technique used when clases with multiple constructors have large portions of similar construction code as well. Although the fact that you "reinitialize" objects rather than just recreate them suggests your design for the objects themselves is flawed, unless they are extremely expensive to destruct.

But wait! You have derived classes, and "SomeFunction" is virtual. When you attempt to initialize a class, it will not be propertly reinitialized. Only the derived version of the SomeFunction initializer will be called to reset the values; any values that would normally be reset by the base version will be left at whatever values they are -- they will not get reinitialized because the base SomeFunction will never run.

Of course, you could call Base::SomeFunction() inside Derived::SomeFunction(), but that would be a lot of wasted effort, especially during construction where you'd "construct" the base class twice. This means SomeFunction must be re-entrant, this is, it cannot simply allocate memory without first making sure any old memory is freed, and this will then increase memory fragmentation because you'll be allocating, perhaps filling, and then deleting, allocating and perhaps filling two new blocks of memory each time you construct an object.

I think the long-term correct approach is to (a) remove the clunky, non-idiomatic "reinitialize" usage of your objects and (b) use constructors, possibly with non-virtual private helper methods if you need "constructor delegation" behavior to initialize your objects.

This trick is just going to make your code a mess.

Share this post


Link to post
Share on other sites

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