0

Let's say I have a template base class like this:

class Util {
public: 
    Util(const std::string& suffix) : name(base_name + "." + suffix) {}
protected:
    const std::string base_name = "Util";
    const std::string name;
};

and I want to create a number of subclasses with different names.

I could do it like this:

class WorkingUtil : public Util {
public:
    WorkingUtil() : Util("Working") {}
};

But not like this:

class Bad1Util : public Util {
public:
    Bad1Util() : Util(suffix) {}
private:
    const std::string suffix = "Bad1";
};

nor this:

class Bad2Util : public Util {
public:
    Bad2Util() : suffix("Bad2"), Util(suffix) {}
private:
    const std::string suffix;
};

Here is a godbolt example and the error:

terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc

What exactly is the problem here? I could do it like the top example, but I want to know why the other ones are not acceptable.

Darkproduct
  • 1,062
  • 13
  • 28
  • 1
    Base classes are initialised before members of the derived class. In both cases, you're attempting to use `suffix` before it is initialised (the constructor of `Util` is invoked before `suffix` is initialised). The behaviour is therefore undefined. The order of you write things in the initialiser list of the derived class constructor (in the case of `Bad2Util`) does not change the order in which initialisation actually happens. – Peter Jun 11 '20 at 12:32
  • @Peter I thought the order of the *initializer-list* would do something. Is this different if you have derived classes or is the order not important at all, and the compiler figures everything out? I see you updated the comment. If you post it as an answer, I'll accept it. – Darkproduct Jun 11 '20 at 12:37
  • @Darkproduct -- the order of items in the initializer list doesn't have any effect. The base classes are constructed first, in the order in which they are listed in the class definition, then the members of the derived class are constructed, in the order in which they are defined in the class definition. – Pete Becker Jun 11 '20 at 14:09

2 Answers2

1

Compile your code with -Wextra and -Wall;

<source>:49:9: warning: field 'suffix' will be initialized after base 'Util' [-Wreorder-ctor]

        suffix("Bad2"),
pvc
  • 1,070
  • 1
  • 9
  • 14
0

If a name is all you want:

Programmatically getting the name of a derived class

Pragmatically, it might be nicer to have an int, so you can switch on it, which might be easiest with a trivial override method like public: int getClassID(){ return constant_# ; }, so there is just one # per class, not an instance variable. The compiler can inline this, so it has the effect of a variable.

  • Unfortunately, I need to initialize a private `ros::NodeHandle` with this name to read only the params for this utility. This means I either have to provide and use a init function for all Utils or do it like my first example. – Darkproduct Jun 11 '20 at 13:00
  • Whatever, lodge the constant in an overridden function and use it in an inherited, common function to filter differently parameters on different derived class objects. Putting the parameters in a tree by target child class might be nice, too. – David G. Pickett Jun 12 '20 at 23:04