如何通过多态基础 class 接口使用模板化子 classes
How to use templated child classes by polymorphic base class interface
我有基本的父 class 和模板化的子 class。
我想使用集合中的 Childs 通过父级的多态接口枚举它们。
我希望在那里调用虚拟(多态)函数 - 但我只有对 Parent::print()
的静态类型调用
#include <iostream>
#include <vector>
using namespace std;
class Parent {
public:
Parent() { cout << " parent ctor "; }
virtual void print() { cout << " Parent::print "; }
};
template <typename T>
class Child : public Parent {
public:
Child(T value) : var(value) { cout << " child ctor "; }
virtual void print() { cout << " Child::print " << var; }
protected:
T var;
};
int main() {
Child<int> myChild(1);
Child<double> myDoubleChild(2.);
vector<Parent> v = {myChild, myDoubleChild};
for (auto i : v) {
i.print();
}
return 0;
}
实际输出:
parent ctor child ctor parent ctor child ctor Parent::print Parent::print
预期输出应包含 "Child::print" 虚函数调用
原因是Object Slicing。
即:
when you assign an object of a subclass to the super class. The superclass knows nothing of the additional information in the subclass, and hasn't got room to store it, so the additional information gets "sliced off"
C++ 中的多态性仅适用于引用和指针。 vector<Parent>
是一个值向量。它们被构造为 Child
实例的副本,并且都是 Parent
类型。基本上它的作用是这样的:
vector<Parent> v = { Parent(myChild), Parent(myDoubleChild) };
如果您将 Parent
设为抽象 class(例如,将 print
更改为纯虚方法),您会看到错误。
您需要一个指针向量,例如:
vector<unique_ptr<Parent>>
如@tchelidze 和@StenSoft 所述,存在两个缺陷:
- 对象切片。蹩脚的错误。 std::vector 使用原始对象
- 多态性仅适用于指针或引用
启用动态调度代码应该是这样的:
#include <iostream>
#include <vector>
using namespace std;
class Parent {
public:
Parent() { cout << " parent ctor "; }
virtual void print() { cout << " Parent::print "; }
};
template <typename T>
class Child : public Parent {
public:
Child(T value) : var(value) { cout << " child ctor "; }
virtual void print() { cout << " Child::print " << var; }
protected:
T var;
};
int main() {
Child<int> myChild(1);
Child<double> myDoubleChild(2.);
// vector<Parent> v = {myChild, myDoubleChild};
vector<Parent*> v = {&myChild, &myDoubleChild};
for (auto i : v) {
// i.print();
i->print();
}
return 0;
}
这给出了所需的输出:
parent ctor child ctor parent ctor child ctor Child::print 1 Child::print 2
如前所述,对象切片是问题所在。使用原始指针向量的替代方法是使用 std::reference_wrapper.
的向量
std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
class Parent {
public:
Parent() { cout << " parent ctor "; }
virtual void print() { cout << " Parent::print "; }
};
template <typename T>
class Child : public Parent {
public:
Child(T value) : var(value) { cout << " child ctor "; }
virtual void print() { cout << " Child::print " << var; }
protected:
T var;
};
int main() {
Child<int> myChild(1);
Child<double> myDoubleChild(2.);
typedef std::reference_wrapper<Parent> refType;
vector<refType> v = {refType(myChild), refType(myDoubleChild)};
for (auto i : v) {
i.get().print();
}
return 0;
}
这段代码的输出是:
parent ctor child ctor parent ctor child ctor Child::print 1 Child::print 2
它不是那么干净(仍然需要特殊的语法)但可能被认为是更地道的 C++。
我有基本的父 class 和模板化的子 class。 我想使用集合中的 Childs 通过父级的多态接口枚举它们。 我希望在那里调用虚拟(多态)函数 - 但我只有对 Parent::print()
的静态类型调用#include <iostream>
#include <vector>
using namespace std;
class Parent {
public:
Parent() { cout << " parent ctor "; }
virtual void print() { cout << " Parent::print "; }
};
template <typename T>
class Child : public Parent {
public:
Child(T value) : var(value) { cout << " child ctor "; }
virtual void print() { cout << " Child::print " << var; }
protected:
T var;
};
int main() {
Child<int> myChild(1);
Child<double> myDoubleChild(2.);
vector<Parent> v = {myChild, myDoubleChild};
for (auto i : v) {
i.print();
}
return 0;
}
实际输出:
parent ctor child ctor parent ctor child ctor Parent::print Parent::print
预期输出应包含 "Child::print" 虚函数调用
原因是Object Slicing。
即:
when you assign an object of a subclass to the super class. The superclass knows nothing of the additional information in the subclass, and hasn't got room to store it, so the additional information gets "sliced off"
C++ 中的多态性仅适用于引用和指针。 vector<Parent>
是一个值向量。它们被构造为 Child
实例的副本,并且都是 Parent
类型。基本上它的作用是这样的:
vector<Parent> v = { Parent(myChild), Parent(myDoubleChild) };
如果您将 Parent
设为抽象 class(例如,将 print
更改为纯虚方法),您会看到错误。
您需要一个指针向量,例如:
vector<unique_ptr<Parent>>
如@tchelidze 和@StenSoft 所述,存在两个缺陷:
- 对象切片。蹩脚的错误。 std::vector 使用原始对象
- 多态性仅适用于指针或引用
启用动态调度代码应该是这样的:
#include <iostream>
#include <vector>
using namespace std;
class Parent {
public:
Parent() { cout << " parent ctor "; }
virtual void print() { cout << " Parent::print "; }
};
template <typename T>
class Child : public Parent {
public:
Child(T value) : var(value) { cout << " child ctor "; }
virtual void print() { cout << " Child::print " << var; }
protected:
T var;
};
int main() {
Child<int> myChild(1);
Child<double> myDoubleChild(2.);
// vector<Parent> v = {myChild, myDoubleChild};
vector<Parent*> v = {&myChild, &myDoubleChild};
for (auto i : v) {
// i.print();
i->print();
}
return 0;
}
这给出了所需的输出:
parent ctor child ctor parent ctor child ctor Child::print 1 Child::print 2
如前所述,对象切片是问题所在。使用原始指针向量的替代方法是使用 std::reference_wrapper.
的向量std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
class Parent {
public:
Parent() { cout << " parent ctor "; }
virtual void print() { cout << " Parent::print "; }
};
template <typename T>
class Child : public Parent {
public:
Child(T value) : var(value) { cout << " child ctor "; }
virtual void print() { cout << " Child::print " << var; }
protected:
T var;
};
int main() {
Child<int> myChild(1);
Child<double> myDoubleChild(2.);
typedef std::reference_wrapper<Parent> refType;
vector<refType> v = {refType(myChild), refType(myDoubleChild)};
for (auto i : v) {
i.get().print();
}
return 0;
}
这段代码的输出是:
parent ctor child ctor parent ctor child ctor Child::print 1 Child::print 2
它不是那么干净(仍然需要特殊的语法)但可能被认为是更地道的 C++。