在没有模板的情况下访问构造函数中的重写字段
Access overridden field in constructor without templates
我无法从 parent C++ 构造函数访问在 child class 中覆盖的字段,我不能使用模板,因为上游项目不使用它们。
这是我尝试用 C++ 重新实现的 Python 原型。该代码包含两个 driver classes - 一个 child 和一个 parent,并且 parent 在 class 期间打印 name
初始化。
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()
这会向控制台打印两个字符串
Unknown
Specific
看起来在 C++ 中无法访问重写的 name
,因为此时 name
不存在 - Call child method from parent constructor。所以也许还有另一种方法可以在初始化时打印 driver name
?
更新(2018):这个问题的原标题是"Print overridden child field during initialization in C++ without templates",因为太宽泛而被关闭。
假设你有两个classes Base和Derived,Base构造函数对Derived一无所知class,因此无法区分这两种类型。
由于您也不能(不应该)在构造函数中调用虚方法,因此常见的模式是创建一个 init 方法:
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"
如您所见,这绝对不是最简单的解决方案。这种情况下使用模板肯定会更好。
尽管您要求在没有模板的情况下执行此操作,但这是从基础 class 构造函数执行此操作的唯一方法。
这是一个如何完成的示例:
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;
}
关于您的评论:
Is it possible to use additional init() function as in @tobspr method, but making name a field instead of function call?
好吧,既然 class 名称是这些 class 的静态 属性,您可以像下面这样使用 static const
字段:
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;
}
甚至简化使用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;
}
我无法从 parent C++ 构造函数访问在 child class 中覆盖的字段,我不能使用模板,因为上游项目不使用它们。
这是我尝试用 C++ 重新实现的 Python 原型。该代码包含两个 driver classes - 一个 child 和一个 parent,并且 parent 在 class 期间打印 name
初始化。
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()
这会向控制台打印两个字符串
Unknown
Specific
看起来在 C++ 中无法访问重写的 name
,因为此时 name
不存在 - Call child method from parent constructor。所以也许还有另一种方法可以在初始化时打印 driver name
?
更新(2018):这个问题的原标题是"Print overridden child field during initialization in C++ without templates",因为太宽泛而被关闭。
假设你有两个classes Base和Derived,Base构造函数对Derived一无所知class,因此无法区分这两种类型。
由于您也不能(不应该)在构造函数中调用虚方法,因此常见的模式是创建一个 init 方法:
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"
如您所见,这绝对不是最简单的解决方案。这种情况下使用模板肯定会更好。
尽管您要求在没有模板的情况下执行此操作,但这是从基础 class 构造函数执行此操作的唯一方法。
这是一个如何完成的示例:
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;
}
关于您的评论:
Is it possible to use additional init() function as in @tobspr method, but making name a field instead of function call?
好吧,既然 class 名称是这些 class 的静态 属性,您可以像下面这样使用 static const
字段:
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;
}
甚至简化使用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;
}