views:

121

answers:

4

Please help me with the following problem:

I have the following classes:

class ChemicalElement
{
private:
    std::string _name;
    void Init(const std::string& name);
public:
    ChemicalElement(const std::string& name);
    ChemicalElement(const ChemicalElement& ce);
};

class CombinationRule
{
private:
    ChemicalElement _ce1;
    ChemicalElement _ce2;
    void Init(const ChemicalElement& ce1, const ChemicalElement& ce2);
public:
    CombinationRule(const ChemicalElement& ce1, const ChemicalElement& ce2);
    CombinationRule(const CombinationRule& rule);
};

The implementation is obvious. I intended to initialize the CombinationRule using the Init method to minimize code duplication. Alas, if I do not use "member initialization list" in each constructor the compiler complains with "error C2512: 'ChemicalElement' : no appropriate default constructor available". Is there an elegant way to solve this error instead of using a default constructor or member initialization list? BTW: if there are any other problems in the classes definition please add it too. Since I'm revisiting C++ I want to be aware of them.

+1  A: 

I think you are required to put a default constructor in any class where you define any other constructors, if you want to use objects of that class in any kind of array or container. The default constructor's implementation can just be an empty/no-op method though.

You shouldn't need to put in a member initialization list (although using a member initialization list can be more efficient in some cases, since that way your member variables are only initialized once, instead of being initialized once via their default constructor, and then written to a second time by your Init() method)

Jeremy Friesner
But I don't want that users (like CombinationRule) will create instances of ChemicalElement using that default constructor. If I add a default constructor it will have to be public?
Ikaso
No, it can be private if that's what you want. (although in that case CombinationRule won't be able to use it unless ChemicalElement declares that Combination rule is his friend)
Jeremy Friesner
Note that CombinationRule will (need to) use ChemicalElement's default constructor unless you use initialization lists in CombinationRule's constructors.
Jeremy Friesner
So what will you do if you have some logic inside Init? You will initialize a ChemicalElement using a default constructor and then call Init?
Ikaso
Yes, that would be the thing to do.
Jeremy Friesner
Ok thanks Jeremy.
Ikaso
+3  A: 

You should implement constructors of CombinationRule as follows so they will use appropriate constructors of ChemicalElement:

CombinationRule::CombinationRule(const ChemicalElement& ce1, 
  const ChemicalElement& ce2) : _ce1(ce1), _ce2(ce2) 
{ 
  ... 
}

CombinationRule::CombinationRule(const CombinationRule& rule) : 
  _ce1( rule._ce1 ), _ce2( rule._ce2 )
{
  ...
}
Kirill V. Lyadvinsky
Thanks for your response. That's what I'll do.
Ikaso
+1  A: 

I think you want this

ChemicalElement * ce1;

I say this because I think its trying to run the default constructor on your CombinationRule and in turn needs to get a ChemicalElement for ce1 and ce2 ... but I could be wrong.

Pretty sure Krill's way is the way to specify the constructor of a variable for a specific constructor BUT i said f that and just made it so ce1 doesn't need to be constructed by the compiler :)

Buttink
This is an option too, but I think it will we a better option for a more complicated case. Thanks.
Ikaso
+1  A: 

In this particular example I would go on with duplication (it's just writing two initializers, nothing to get obsessive about).

But assuming the real story is more complex: use OO facilities to avoid code duplication.

class CombinationRule : public ElementPair ...

or

class Combination { ElementPair twoElements; ...}

Where ElementPair contains two ChemicalElements and a single constructor (with common code), and Combination rule constructors initialize using constructor of ElementPair.

There are other approaches: initializing members with some InvalidChemicalElement instance or using pointers (auto_ptr) with NULL for InvalidChemicalElement.

ima
I didn't understand how will the ElementPair solution solve the default constructor error?
Ikaso
Place common code into ElementPair constructor instead of Init method. You can call base or member constructor in initializer lists of all CombinationRule constructors - thus, no default constructor for ChemicalElement is needed.
ima
CombinationRule(a) : ElementPair(a.first, a.second) instead of CombinationRule(a) { Init(a.first, a.second); }
ima