问题
I can't access field overridden in child class from parent C++ constructor and I can not use templates, because upstream project doesn't use them.
This is working Python prototype that I try to reimplement in C++. The code contains two driver classes - one child and one parent, and parent prints name of the class during initialization.
class Driver(object):
name = "Unknown"
def __init__(self):
print(self.name)
class SpecificDriver(Driver):
name = "Specific"
def __init__(self):
super(SpecificDriver, self).__init__()
Driver()
SpecificDriver()
This prints two strings to console
Unknown
Specific
Looks like in C++ can't access overridden name, because the name doesn't exist at this point - Call child method from parent constructor. So maybe there is another way to get driver name printed on initialization?
UPDATE (2018): The original title for this question was "Print overridden child field during initialization in C++ without templates" and it was closed as too broad.
回答1:
Despite you're asking to do this without templates, it's the only way to do this from a base class constructor.
Here's a sample how it should be done:
struct IDriver {
// Public virtual API:
virtual void func1() = 0;
// ...
virtual ~IDriver() {}
};
template<typename Derived>
class Driver : public IDriver {
public:
Driver() {
std::cout << "Driver" << std::endl;
std::cout << static_cast<Derived*>(this)->name() << std::endl;
}
};
class SpecificDriver : public Driver<SpecificDriver> {
public:
// Public virtual API:
virtual void func1();
std::string name() const { return "SpecificDriver"; }
// or use typeid(SpecificDriver).name() if you prefer
};
int main() {
SpecificDriver sd;
}
Live Demo
As for your comment:
Is it possible to use additional init() function as in @tobspr method, but making name a field instead of function call?
Well, since the class name is a static property of these classes anyway, you can use a static const field like follows:
template<typename Derived>
class Driver : public IDriver {
public:
Driver() {
std::cout << name << std::endl;
std::cout << Derived::name << std::endl;
}
private:
static const std::string name;
};
template<typename Derived>
const std::string Driver<Derived>::name = "Driver";
class SpecificDriver : public Driver<SpecificDriver> {
public:
static const std::string name;
};
const std::string SpecificDriver::name = "SpecificDriver";
int main() {
SpecificDriver sd;
}
Live Demo
or even simplified use typeid():
#include <iostream>
#include <string>
#include <typeinfo>
template<typename Derived>
class Driver {
public:
Driver() {
std::cout << typeid(*this).name() << std::endl;
std::cout << typeid(Derived).name() << std::endl;
}
};
class SpecificDriver : public Driver<SpecificDriver> {
};
int main() {
SpecificDriver sd;
}
Live Demo
回答2:
Assuming you have two classes Base and Derived, the Base constructor does not know anything about the Derived class, which makes it impossible to distinguish between both types.
Since you also can not (should not) call virtual methods in the constructor, a common pattern is to make an init method:
struct Base {
virtual std::string get_name() { return "Base"; }
void init() { std::cout << get_name(); }
};
struct Derived : public Base {
virtual std::string get_name() { return "Derived"; }
};
// Later on ..
Base b;
b.init(); // Should print "Base"
Derived d;
d.init(); // Should print "Derived"
As you can see, this is definitely not the simplest solution. Using templates in this case would certainly be better.
来源:https://stackoverflow.com/questions/36964242/access-overridden-field-in-constructor-without-templates