为什么这个 C++ 继承代码示例的行为是这样的
Why is this C++ inheritance code sample behaves like this
我有一个代码示例,它对我来说很奇怪。
通过 C++ 中的继承,可以使用纯虚函数(又名接口)声明指向基 class 的指针数组,并在其上调用派生成员函数;
class Base {
public:
virtual void call() = 0;
};
class Derived1 : public Base {
public:
void call() override final {
std::wcout << L"derived 1" << std::endl;
}
};
class Derived2 : public Base {
public:
void call() override final {
std::wcout << L"derived 2" << std::endl;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
b[i]->call();
}
return 0;
}
给出:
derived 1
derived 2
正如计划的那样。
但是当我尝试遵循代码示例时,它让我有点困惑:
class Base {
public:
virtual Base* print() = 0;
template<typename T>
Base& operator<<(const T &_val) {
std::wcout << L" d0 << " << _val;
return *this;
}
};
class Derived1 : public Base {
public:
Derived1* print() override final {
return this;
}
template<typename T>
Derived1& operator<<(const T &_val) {
std::wcout << L" d1 << " << _val;
return *this;
}
};
class Derived2 : public Base {
public:
Derived2* print() override final {
return this;
}
template<typename T>
Derived2& operator<<(const T &_val) {
std::wcout << L" d2 << " << _val;
return *this;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}
输出为:
8Derived1 d0 << 7 d0 << 7
8Derived2 d0 << 7 d0 << 7
这意味着只有 Base 的 operator<< 被调用(但是 prints() return 类型似乎是正确的)。
问题是为什么它的行为如此?
更新:
看来我需要没有虚函数的静态多态性。但这怎么可能实现呢?
我需要一组不同的 Derived classes 来在 operator<< 中对任何数据类型执行操作。
UPD2:
看来我可以对 operator<< 参数使用类型擦除。但是我怎样才能在派生的 operator<< 中恢复类型呢? (例如,如果我建议使用 boost::any)
您的 operator << ()
不是虚拟的,因此,如果您调用基 class 指针的此运算符,将始终调用基 class 实现。
那是因为它是模板方法,模板方法不能virtual
(参见Can a C++ class member function template be virtual?)。
解决方案是为每个支持的数据类型编写虚拟专用 operator << ()
(如果您真的希望它们在继承的 class 之间有所不同)。
这是更改后的代码:
class Base {
public:
virtual Base* print() = 0;
virtual Base& operator<<( int _val ) = 0;
};
class Derived1 : public Base {
public:
Derived1* print() override final {
return this;
}
Base& operator<<( int _val ) override final {
std::wcout << L" d1 << " << _val;
return *this;
}
};
class Derived2 : public Base {
public:
Derived2* print() override final {
return this;
}
Base& operator<<( int _val ) override final {
std::wcout << L" d2 << " << _val;
return *this;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}
另一种方法是,仅通过 class 层次结构(此处:class 名称)实现不同的部分,并使用模板运算符进行通用调用。
#include <iostream>
#include <string>
class Base {
public:
virtual Base* print() = 0;
virtual const wchar_t* className() const = 0;
template<typename T>
Base& operator<<(const T &_val) {
std::wcout << L" " << className() << L" << " << L" << " << _val;
return *this;
}
};
class Derived1 : public Base {
public:
const wchar_t* className() const override final { return L"d1"; }
Derived1* print() override final {
return this;
}
};
class Derived2 : public Base {
public:
const wchar_t* className() const override final { return L"d2"; }
Derived2* print() override final {
return this;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}
最后一种方法是根据 class 类型进行 RTTI 转换,并根据 class 类型调用专门的 operator << ()
。
#include <iostream>
class Derived1;
class Derived2;
class Base {
public:
virtual Base* print() = 0;
};
class Derived1 : public Base {
public:
Derived1* print() override final {
return this;
}
template<typename T>
Derived1& operator<<(const T &_val) {
std::wcout << L" d1 << " << _val;
return *this;
}
};
class Derived2 : public Base {
public:
Derived2* print() override final {
return this;
}
template<typename T>
Derived2& operator<<(const T &_val) {
std::wcout << L" d2 << " << _val;
return *this;
}
};
template<typename T>
Base& operator<<( Base& _base, const T &_val) {
if( typeid( _base ) == typeid( Derived1 ))
return dynamic_cast<Derived1*>(&_base)->operator << (_val);
else
return dynamic_cast<Derived2*>(&_base)->operator << (_val);
}
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}
我有一个代码示例,它对我来说很奇怪。 通过 C++ 中的继承,可以使用纯虚函数(又名接口)声明指向基 class 的指针数组,并在其上调用派生成员函数;
class Base {
public:
virtual void call() = 0;
};
class Derived1 : public Base {
public:
void call() override final {
std::wcout << L"derived 1" << std::endl;
}
};
class Derived2 : public Base {
public:
void call() override final {
std::wcout << L"derived 2" << std::endl;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
b[i]->call();
}
return 0;
}
给出:
derived 1
derived 2
正如计划的那样。 但是当我尝试遵循代码示例时,它让我有点困惑:
class Base {
public:
virtual Base* print() = 0;
template<typename T>
Base& operator<<(const T &_val) {
std::wcout << L" d0 << " << _val;
return *this;
}
};
class Derived1 : public Base {
public:
Derived1* print() override final {
return this;
}
template<typename T>
Derived1& operator<<(const T &_val) {
std::wcout << L" d1 << " << _val;
return *this;
}
};
class Derived2 : public Base {
public:
Derived2* print() override final {
return this;
}
template<typename T>
Derived2& operator<<(const T &_val) {
std::wcout << L" d2 << " << _val;
return *this;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}
输出为:
8Derived1 d0 << 7 d0 << 7
8Derived2 d0 << 7 d0 << 7
这意味着只有 Base 的 operator<< 被调用(但是 prints() return 类型似乎是正确的)。
问题是为什么它的行为如此?
更新:
看来我需要没有虚函数的静态多态性。但这怎么可能实现呢? 我需要一组不同的 Derived classes 来在 operator<< 中对任何数据类型执行操作。 UPD2:
看来我可以对 operator<< 参数使用类型擦除。但是我怎样才能在派生的 operator<< 中恢复类型呢? (例如,如果我建议使用 boost::any)
您的 operator << ()
不是虚拟的,因此,如果您调用基 class 指针的此运算符,将始终调用基 class 实现。
那是因为它是模板方法,模板方法不能virtual
(参见Can a C++ class member function template be virtual?)。
解决方案是为每个支持的数据类型编写虚拟专用 operator << ()
(如果您真的希望它们在继承的 class 之间有所不同)。
这是更改后的代码:
class Base {
public:
virtual Base* print() = 0;
virtual Base& operator<<( int _val ) = 0;
};
class Derived1 : public Base {
public:
Derived1* print() override final {
return this;
}
Base& operator<<( int _val ) override final {
std::wcout << L" d1 << " << _val;
return *this;
}
};
class Derived2 : public Base {
public:
Derived2* print() override final {
return this;
}
Base& operator<<( int _val ) override final {
std::wcout << L" d2 << " << _val;
return *this;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}
另一种方法是,仅通过 class 层次结构(此处:class 名称)实现不同的部分,并使用模板运算符进行通用调用。
#include <iostream>
#include <string>
class Base {
public:
virtual Base* print() = 0;
virtual const wchar_t* className() const = 0;
template<typename T>
Base& operator<<(const T &_val) {
std::wcout << L" " << className() << L" << " << L" << " << _val;
return *this;
}
};
class Derived1 : public Base {
public:
const wchar_t* className() const override final { return L"d1"; }
Derived1* print() override final {
return this;
}
};
class Derived2 : public Base {
public:
const wchar_t* className() const override final { return L"d2"; }
Derived2* print() override final {
return this;
}
};
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}
最后一种方法是根据 class 类型进行 RTTI 转换,并根据 class 类型调用专门的 operator << ()
。
#include <iostream>
class Derived1;
class Derived2;
class Base {
public:
virtual Base* print() = 0;
};
class Derived1 : public Base {
public:
Derived1* print() override final {
return this;
}
template<typename T>
Derived1& operator<<(const T &_val) {
std::wcout << L" d1 << " << _val;
return *this;
}
};
class Derived2 : public Base {
public:
Derived2* print() override final {
return this;
}
template<typename T>
Derived2& operator<<(const T &_val) {
std::wcout << L" d2 << " << _val;
return *this;
}
};
template<typename T>
Base& operator<<( Base& _base, const T &_val) {
if( typeid( _base ) == typeid( Derived1 ))
return dynamic_cast<Derived1*>(&_base)->operator << (_val);
else
return dynamic_cast<Derived2*>(&_base)->operator << (_val);
}
int main() {
Base* b[2];
b[0] = new Derived1;
b[1] = new Derived2;
for (int i = 0; i < 2; ++i) {
std::wcout << typeid(*b[i]->print()).name();
*b[i]->print() << 7 << 7;
std::wcout << std::endl;
}
return 0;
}