In Effective C++, 3rd Edition, Page 173~175, Scott Meyers talked of alternatives to virtual functions using the strategy pattern:
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:
typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
explicit GameCharacter(HealthCalcFUnc hcf = defaultHealthCalc)
: healthFunc(hcf)
{}
int healthValue const
{ return healthFunc(*this) }
...
private:
HealthCalcFunc healthFunc;
};
...
struct HealthCalculator {
int operator()(const GameCharacter&) const
{ ... }
};
...
class EyeCandyCharacter: public GameCharacter {
explicit EyeCandyCharacter(HealthCalcFunc hcf=defaultHealthCalc)
:GameCharacter(hcf)
{ ... }
...
}
...
EyeCcandyCharacter ecc(HealthCalculator());
The last statement is to illustrate how to use a health calculation function object in the constructor of EyeCandyCharacter class.
My question is, the constructor of EyeCandyCharacter class requires some function that takes a parameter compatible with a const GameCharacter& and returns something convertible to an int.
Is this supported/implemented by operator() defined in struct HealthCalculator? I don't quite understand the meaning of this overloaded operator here.
Another question of mine here is that the initializer list in the constructor of the derived class usually only initializes the data members of itself (though I know the base part of the derived class is also intialized implicitly). How come the base class GameCharacter appears in the initializer of derived class EyeCandyCharacter?
In your first question:
My question is, the constructor of EyeCandyCharacter class requires
some function that takes a parameter compatible with a const
GameCharacter& and returns something convertible to an int. ... Is
this supported/implemented by operator() defined in struct
HealthCalculator?
Yes, it is supported. You must know/remember that HealthCalculator is a functor. It implements the operator () to "simulate" the syntax of calling traditional functions. Its operator () takes a const GameCharacter& and returns an int, which is compatible with what EyeCandyCharacter (and subsequently GameCharacter) wants.
In your second question:
How come the base class GameCharacter appears in the initializer of
derived class EyeCandyCharacter?
That initializes the base class of EyeCandyCharacter which is GameCharacter by calling GameCharacter's constructor. Not doing this makes EyeCandyCharacter's constructor call GameCharacter's default constructor, which isn't defined, and therefore will result in an error.
As a sidenote, you can now, in C++11, directly use std::function which have roughly the same functionality as std::tr1::function.
For your second question:
Another question of mine here is that the initializer list in the constructor of the derived class usually only initializes the data members of itself (though I know the base part of the derived class is also intialized implicitly). How come the base class GameCharacter appears in the initializer of derived class EyeCandyCharacter?
No, if your base class does not have default constructor defined, but has other constructors defined instead, you have to call based class constructor to initialize base class members. Since in this case, compile will not generate a default constructor for you. In other words,
the base part of the derived class is NOT always initialized implicitly
Related
I have an empty base class with a virtual function. Is there any way I can avoid manually implementing the constructors of Derived in order to be able to initialize it?
struct Base
{
virtual int f(int) const = 0;
virtual ~Base() = 0;
};
Base::~Base() {}
struct Derived: public Base
{
int v;
int f(int) const override{ return v;};
};
int main()
{
return Derived{5}.f(3);
}
Is there any way I can avoid manually implementing the constructors of Derived in order to be able to initialize it?
No. Having a base class and virtual functions causes Derived not being an aggregate.
If you remove the virtual functions, you have aggregates and you can
return Derived{{}, 5}.f(3);
// ^^ explicitly initialize base class
but as this is not the point of your question, this is no solution to it.
Both the base class and the derived class has an implicit default constructor (as well as copy and move), and there is no need to manually implement those.
Neither class is an aggregate, so they cannot be initialised using list initialisation without implementing a custom constructor that accepts compatible list of arguments. This is why your example program does not work. Neither class can become an aggregate without removing the virtual member function.
So, your options are:
Don't use list initialisation, except with an empty list of parameters which is a special case that performs value initialisation which invokes the default constructor
or provide a custom constructor for the list of arguments that you want to pass
or remove the virtual member function so that the classes become aggregates, and their members can then be initialised directly with list initialisation
I've got following program:
#include<iostream>
using namespace std;
struct Base01{
int m;
Base01():m(2){}
void p(){cout<<m<<endl;}
};
struct Derived01:public Base01{
Derived01():m(3){}
};
struct Derived02:virtual public Base01{
Derived01():m(4){}
};
struct my: Derived01,Derived02{
my():m(5){}
};
int main(){
return 0;
}
Both gcc/clang reports compilation error.
I just wish to know what's the language design consideration here, why derived class can only call base class ctor in initialization list, but cannot use base class members directly?
What you do in the constructor initializer list is initialization. It is something that has to be done only once in the lifetime of the object. In general case, that's what starts the objects lifetime.
Base class's constructor (which finished working before your derived class's constructor-proper became active) has already initialized all direct subobjects of the base class. It has already started their lifetimes. If you attempt to reach-in and initialize a direct subobject of base class from the derived class's constructor, that will obviously be the second initialization of the same object. This is completely unacceptable in C++. The language design generally does not allow you to initialize something the second time.
In your case the subobject in question has fundamental type int, so it is hard to see the harm in such "re-initialization". But consider something less trivial, like an std::string object. How do you suggest the derived class should "undo and redo" the initialization already performed by the base class? And while formally it is possible to do it properly, constructor initializer lists are not intended for that purpose.
In general case doing something like that would require a language feature that would allow user to tell base class's constructor something along the lines of "please, leave this subobject of yours uninitialized, I will reach-in and initialize it later from the derived class". However, C++ does not provide users with such capability. A vaguely similar feature exists in virtual base class initialization, but it serves a very specific (and different) purpose.
The proper way to do this in C++ is to pass that value to the base class constructor. Your Base01 class needs an additional constructor that takes the desired value for m. Something like this:
struct Base01{
int m;
Base01():m(2){}
// Added this:
Base01(int mVal) : m(mVal) {}
void p(){cout<<m<<endl;}
};
struct Derived01:public Base01{
Derived01() : Base01(3) {} // Calling base constructor rather than
// initializing base member
};
struct Derived02:virtual public Base01{
Derived01() : Base01(4){} // Same here
};
struct my: Derived01,Derived02{
my(): Base01(5){} // And here.
};
As AnT said, you can't initialize twice--but you can set it up so that things are initialized the way you want in the first place by doing as above.
You certainly can use a base class member in the ctor-initializer list:
struct Base
{
int x;
Base(int x) : x(x) {}
};
struct Derived
{
int y;
Derived() : Base(7), y(x) {}
}
Here, the base member x appears in the initializer for the derived member y; its value will be used.
AnT has done a very nice job explaining why the ctor-initializer list can't be used to (re-)initialize members of base subobjects.
The fundamental language design considerations in this are separation of concerns (avoiding making a base class depend on its derived classes), and that a base class is responsible for initialising its own members (and any bases it has).
A related consideration is that members of the base class don't exist - as far as the derived class constructor is concerned - before the base class constructor completes. If an initialiser list of a derived class was able to reach in and initialise a base class member, then there are two possible consequences
If the base class constructor has not been invoked, its members will not exist when the derived class tries to initialise them.
If the base class constructor has been invoked, the member has been initialised. Initialisation (as distinct from assignment to reinitialise) happens once in the lifetime of an object, so it does not make sense to initialise it again.
Neither of these possibilities really make sense in practice, unless the base class is poorly designed (e.g. its constructors do not properly initialise its members). The sort of machinery needed so they might make sense (e.g. changing order of construction of base classes in a hierarchy, dependent on what member a derived class is trying to initialise) would make compiler machinery more complicated (e.g. being able to both control and track the order of construction of base class members, in case a derived class should choose to reach in), and also mean that the order of construction of classes would depend on derived classs). This would introduce a dependency of the base class behaviour (the means by which it is initialised) on derived classes.
The simpler means is for the base class to provide a constructor that properly initialised the member in question, and for the derived class constructor to invoke that base class constructor in its initialiser list. All of the above (hypothetical) considerations then go away.
If my base class's constructor takes parameters, how do I create a class that inherits from it, and executes the base class's constructor using the derived class's constructor?
For example, assuming my base class's instructor takes an integer, and does a cool trick with it, how would I make it so all classes that inherit from it can do the same
Pseudo-code
baseClass instance(99); //does an awesome trick, nice
derivedClass instance(99); //<-- should do the same as above
As I have it now, I have my base class's constructor defined, but for the derived class, I left its constructor blank, since I want it to do exactly what the base constructor does, and nothing more. But, this gave me an error along the lines of: no matching function call to the 'baseClass()', candidates are: baseClass(int), candidate expects 1 argument, 0 provided
Do I have to make the base class's constructor virtual or something?
This is easy in the current C++ standard, but it still requires some action on your part:
struct baseClass
{
explicit baseClass(int);
};
struct derivedClass : baseClass
{
using baseClass::baseClass; // inherit *all* of baseClass' constructors
};
In the previous standard, C++03, you have to implement the constructor in the derived type and call the base class's one in the initialization list:
struct derivedClass : baseClass
{
explicit derivedClass(int i) : baseClass(i) {}
};
Note: I use struct here to save typing: default member access and inheritance is public.
The idea is that derivedClass will need to call baseClass constructor anyway, since it needs to to complete its construction. The error you get is due to the fact that, without calling a the base constructor explicitly, the compiler is trying to call the default one.
However, in your case, it does not exist, since you already defined a constructor that takes an int. To do what you want, a solution could be to do this:
class derivedClass : baseClass {
public:
derivedClass(int x) : baseClass(x) {}
};
in this program :http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/example/chat/chat_server.cpp
class chat_session
: public chat_participant,
chat_session inherits chat_participant
in one of the calls a shared_ptr to chat_session is sent to the join method
room_.join(shared_from_this());
which is defined as
void join(chat_participant_ptr participant)
so how does the example above translate to particpant being a base class pointer to the inherited class instance?
My understnading of base class pointer to inherited class instance is the from the Virtual members
example here
http://www.cplusplus.com/doc/tutorial/polymorphism/
---edit---
An example would be great if somebody could explain how a pointer to base class can be defined in the function arguments even if we are not using shared_ptrs
Smart pointers are supposed to behave like regular raw pointers in this respect. With raw pointers, you can have a function foo() like the one below:
void foo(B* pBase);
And - given a class D that derives from B - pass to it a pointer of type D*:
class D : public B { ... };
// ...
D obj;
foo(&obj); // OK!
This is simply how derived-to-base conversion works, and its fundamental for polymorphism. Now smart pointers are meant to emulate this mechanism, so that given:
void foo(shared_ptr<B> pBase);
You could do:
shared_ptr<D> pObj = make_shared<D>();
foo(pObj); // OK!
Technically, the way this behavior is achieved for the shared_ptr class template is to have a user-defined constructor template that carries out the implicit conversion:
template<class T> class shared_ptr {
public:
// ...
template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;
// ...
};
This converting constructor will actually participate to overload resolution only if Y is convertible to T. ยง 20.7.2.2.1/17 of the C++11 Standard specifies:
Requires: The second constructor shall not participate in the overload resolution unless Y* is implicitly
convertible to T*.
This is typically achieved by using SFINAE constraints on the function template.
There is an implicit conversion (by means of a non-explicit constructor) from shared_ptr<T> to shared_ptr<U> if and only if
U is void or
U is an accessible base class of T
I have a purely virtual class defined as such:
class BaseClass {
protected:
const int var;
public:
void somefun() = 0; // what I mean by a purely virtual class
// stuff...
};
If I don't add a constructor defined as such:
BaseClass(const int & VAR) : var(VAR) {};
that I would have to subsequently use in ever derived class, my derived class can't initialize the const variable var to whichever value it wants to. Now I actually understand what's going on here. Before constructing a derived class, a constructor of the base class is called, at which point const member variables must be initialized. My question is not a "how do I make my code work" kind of question, that's already been done. My question is about why the compiler thinks it's necessary. For a purely virtual class, shouldn't I be allowed to write something like:
class DerivedClass : BaseClass {
public:
DerivedClass() : var(SOME_VALUE) {};
}
If the compiler knows that a call to a BaseClass constructor will necessarily be followed by a call to some derived class constructror (since an object of abstract type can never be instantiated) shouldn't it give us a bit more leeway?
Is this all a consequence of how C++ chooses to get around the Diamond problem? Even if that was the case, shouldn't the compiler at least somehow allow for the possibility that const member variable of purely virtual functions will be defined in derived classes? Is that too complicated or does that mess with the C++ solution to the Diamond problem?
Thanks for the help everyone.
It's not "purely virtual" (whatever you mean by that) - it contains a data member.
Class members can only be initialised by the initialiser list of a constructor of that class, not of a derived class. That's how object initialisation is specified: all members that are initialised, are initialised before the constructor body begins.
Constant objects must be initialised, since they can't be assigned a value later.
Therefore, a class with a constant data member must initialise it in each constructor.
For a purely virtual class, shouldn't I be allowed to write something
like
No, but you can(and in this case should) write something like this:
class DerivedClass : BaseClass {
public:
DerivedClass() : BaseClass(SOME_VALUE) {};
};
The construction of an object occurs in a specific order. The base class must be fully constructed before the constructor of a derived class is run, so that the derived constructor is working with a fully formed and valid base object. If the initialization of base member variables were put off until the construction of the derived class, this invariant would be broken.