views:

343

answers:

3

I have this definition in a header file:

class Owner
{
private:
 // Fields
 Child* _myChild1;
public:
 // Constructors
 Owner();
 Owner(const char childName[]);
};

and this implementation:

Owner::Owner(const char childName[])
{
//do some operations - children must be created after these ops
_myChild = new Child(childName);
}

and this main() function:

int main()
{
Owner("child1"); 
}

Some questions, and please bear with me here, I'm just starting out with C++ ..

  • Given that the child classes are known at compile time am I right in thinking that I don't need to create them with 'new' on the heap? If so how? I have tried using this syntax in the Owner implementation but the compiler moans ('term does not evaluate to a function..'):

_myChild(childName);

  • However, using this syntax in the implementation is ok, why?

Child _myChild(childName);

  • Is the paradigm that I'm using correct? In other words, as a general rule, if one class wraps another does the owner only ever hold pointers to the classes it wraps?
  • How would you more experienced guys do it?

Thanks for any advice..

+4  A: 

What you're sort of describing here is "composition", where one class resides in another class. Here, Owner contains a pointer to a Child. So the actually allocation and initialization of the instance of Child must be done somewhere at runtime.

If you want Owner to contain an instance of a Child that you don't have to allocate on the heap with new, simply declare the member variable as:

Child _myChild1;

rather than as a pointer.

Charles Salvia
+5  A: 

Like this:

class Owner
{
    private:        // Fields
        Child   _myChild1;
    public:        // Constructors
        Owner();
        Owner(const char childName[]);
};

Owner::Owner()
    :_myChild1("Default Name")
{}

Owner::Owner(const char childName[])
    :_myChild1(childName)
{}

// Without more info about what you are doing it is hard to tell
// But a trivial change could be

Owner::Owner()
// Child defautl constructor called here
{
    // Do processing.
    _myChild1.updateName(name);
}

Owner::Owner(const char childName[])
// Child defautl constructor called here
{
    // Do processing.
    _myChild1.updateName(std::string("X") + childName);
}

The question is what kind of processing do you need done before the child.

As a side not:

  • avoid underscore as the first character in a member name.
    Most of the time it is OK but there are sitations where it is not. So best to just avoid them.
  • Rather than passing an array of characters pass a std::string const&
Martin York
Minor typo, the field is called _myChild1
Rickard
ok, so the code is more complicated than I have presented. I have to parse the name and some other parameters and these then get passed to the correct child (there are multiple instances) so i can't use the `:_instance(args)` construction, the initialisation must be done in the body. Given that, how should I approach it? and thanks for the advice.
jamieQ
You need to add more information to the question to get a good responce. There are lots of ways of doing this but the more exact you get the better people will be able to reply.
Martin York
sorry, yes, it's a balance between making your question readable and giving enough information and i probably didnt get that balance quite right here :)
jamieQ
If by "there are multiple instances", you mean that there are multiple child classes, and the right one is selected in each compiler call, then you need to heap-allocate those, and just keep a pointer to it. You might do best to use an auto_ptr, so that you needn't write the `delete` in your destructor.
Novelocrat
@Novelcrat: Disagree with that. You only need a pointer if the number of children is not known at compile time at even then there are probably better solutions (like a vector of children).
Martin York
Yes, there are multiple children and each one performs a specific role in relation to the others. The input is unreliable so must be parsed and handed to the children such that their roles are correctly defined. Martin, if I dont want to use functions to set the fields of the children, is the non heap allocated syntax: `Child _myChild(childName);`? Is this the case, even though I have defined the fields previously in the header?
jamieQ
+2  A: 

Basically, how it works is thus:

  • If the Child class is fully defined before the Owner class, it can be included in the Owner in it's entirety, or as a pointer, whichever you prefer.
  • If the Child class is not fully defined before the Owner class, it would have to be forward declared and can only be included as a pointer. This would have to be allocated on the heap with new.

If you do include a class in it's entirety, it will be constructed at the same time as the owner object is constructed. Unless explicitly told otherwise, the compiler will use the default constructor for this.

Option 1:

// will create a Child object using the default constructor.
// this is done even before doStuff() is called.

Owner::Owner(const char childName[]) {
  doStuff();
}

Option 2:

// will create a Child object using the Child(const char*) constructor.
// again, done before doStuff() is called.
Owner::Owner(const char childName[]): _myChild(childName) {
  doStuff()
}

You can't use the _myChild(childName); syntax inside the constructor itself, since _myChild has already been constructed before it ever gets that far. Child _myChild(childName); is legal, but it creates a new local object named _myChild rather than modifying the class member _myChild. This is probably not the intended result.

If you want to assign a new value to _myChild after it's constructed, do one the following:

  • Preferred method: Modify the existing object somehow (such as overloading the assignment operator to allow you to do _myChild = childName;, or using some form of _myChild.setName(childName); function).
  • Alternate method: Use _myChild = Child(childName); to create a new Child and assign it to the member variable.

This second option, although functional, is inefficient since it requires constructing the object twice for no good reason.

goldPseudo
thanks gold, this is the info i was looking for :)
jamieQ