从派生的 class c++ 获取变量
Obtain variable from derived class c++
只有当 class 是特定派生的 class 时,我才会做某事。那就是我有:
class X{
int id;
}
class A: public X{
void run();
}
class B: public X{
int lala;
}
我想做一些事情:
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for(int i = 0; i<types.size(); i++){
if(types[i].isType(A)) {types[i].run();}
}
for(int i = 0; i<types.size(); i++){
if(types[i].isType(B)) {var = lala;}
}
}
我不希望 class B 有任何等同于 运行() 的东西,我也不希望 class A 有等同于 lala 的东西。
我知道 Fortran 有一个解决方法
select type ( x => var )
class is ( A )
x.run()
end select
但我不确定我在 C++ 中的选项是什么。
谢谢
您可以使用 dynamic_cast
检查基础 class 指针是否可转换为派生实例。
另一种选择是使用 returns class 的 typeinfo
的虚函数,然后使用该信息将指针转换为可转换类型。根据 dynamic_cast
的实施方式,此 可能 性能更高。因此,如果您想尝试查看此方法在您的平台上是否更快,则可以使用它。
正如@Jarod42 指出的那样,您需要有一个虚函数,在这种情况下是析构函数,dynamic_cast
才能工作。此外,您只需要一个虚拟析构函数来避免删除实例时出现未定义的行为。
例子
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
struct A {
virtual ~A() {
}
virtual const std::type_info& getTypeInfo() const {
return typeid(A);
}
};
struct B : public A {
virtual const std::type_info& getTypeInfo() const override {
return typeid(B);
}
};
struct C : public A {
virtual const std::type_info& getTypeInfo() const override {
return typeid(C);
}
};
int main()
{
std::vector<A*> data;
data.push_back(new A);
data.push_back(new B);
data.push_back(new C);
for (auto& val : data) {
if (val->getTypeInfo() == typeid(A)) {
std::cout << "A";
}
else if (val->getTypeInfo() == typeid(B)) {
std::cout << "B";
}
else if (val->getTypeInfo() == typeid(C)) {
std::cout << "C";
}
std::cout << std::endl;
}
for (auto& val : data) {
delete val;
}
}
您正在寻找 dynamic_cast
.
#include <vector>
using namespace std;
class X {
public:
int id;
virtual ~X() = default;
};
class A : public X {
public:
void run() {}
};
class B : public X {
public:
int lala;
};
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for(int i = 0; i<types.size(); i++){
if (auto ta = dynamic_cast<A *>(types[i])) {
ta->run();
}
}
for(int i = 0; i<types.size(); i++){
if (auto tb = dynamic_cast<B *>(types[i])) {
var = tb->lala;
}
}
}
也可以在此处查看实际效果:https://onlinegdb.com/B1d29P5if。
我不得不修复代码的其他一些问题。由于它们不是你问题的一部分,我不会在这里澄清,但欢迎你提出任何不清楚的问题。
编辑:上面的解决方案有内存泄漏,我没有修复,因为问题不需要它。为了完整起见,这里是修复了内存泄漏的主要函数 (https://onlinegdb.com/ByeOmu9iz):
int main() {
vector<unique_ptr<X>> types;
types.emplace_back(new A);
types.emplace_back(new B);
int var = 0;
for(int i = 0; i < types.size(); ++i) {
if (auto ta = dynamic_cast<A *>(types[i].get())) {
ta->run();
}
}
for(int i = 0; i < types.size(); ++i) {
if (auto tb = dynamic_cast<B *>(types[i].get())) {
var = tb->lala;
}
}
}
请注意,这是一个 C++11 解决方案。
如果您使用的是更旧的编译器,则必须像在原始解决方案中一样继续使用普通指针,并在最后通过对每个元素调用 delete
手动释放内存向量。 (并希望在您到达该步骤之前没有任何异常抛出。)
您还必须将 auto ta
替换为 A* ta
,将 auto tb
替换为 B* tb
。
这个问题的现代 C++17 解决方案是使用变体向量,即 std::vector<std::variant<A, B>>
。为此,您需要一个现代编译器。
这是一个完整的示例,基于 std::variant
documentation:
#include <vector>
#include <variant>
#include <iostream>
class X {
int id;
};
class A: public X {
public:
void run() {
std::cout << "run\n"; // just for demonstration purposes
}
};
class B: public X {
public:
B(int lala) : lala(lala) {} // just for demonstration purposes
int lala;
};
int main() {
std::vector<std::variant<A, B>> types;
types.push_back(A()); // no more new!
types.push_back(B(123)); // no more new!
int var = 0;
for (auto&& type : types) {
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, A>) {
arg.run();
} else {
var = arg.lala;
}
}, type);
}
std::cout << var << '\n'; // just for demonstration purposes
}
作为一个不错的奖励,这个解决方案优雅地摆脱了动态分配(不再有内存泄漏,不需要智能指针)。
我有两个想法....
为什么不使用一个共享方法 return 作为一个值来提供关于它是 A 还是 B 的上下文?例如,如果预计 lala return 的值仅为 0 或更大,则您可以使用 void 运行() 而不是 int 运行() 和 return -1次。
class X {
int id;
virtual int run() = 0; //Assuming X isn't meant to be instantiated
}
class A: public X {
// Return -1 to differentiate between As and Bs
int run() { return -1; }
}
class B: public X {
int lala;
int run() { return lala;}
}
那么你有...
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0, temp = 0;
for( int i = 0; i<types.size(); i++ ) {
if( (temp = types[i].run()) != -1 )
var = temp;
....
}
}
同样,仅当 lala 永远不会期望 return 特定范围的值时才有效。
您还可以在创建 A 或 B 时在 X 中隐藏信息,以跟踪您拥有的内容。
class X {
int id;
bool isA;
}
class A: public X {
A() : isA(true) { };
void run();
}
class B: public X {
B() : isA(false) { } ;
int lala;
}
那么你有...
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for( int i = 0; i<types.size(); i++ ) {
if( types[i].isA == true ) {
types[i].run();
}
else {
var = types[i].lala;
}
}
当然,如果您希望添加 C、D、E...,它就不再值得了,但对于只有两个派生的 classes 来说,还算不错。
我会基于这样一个事实来证明这一点:用户已经不得不查看派生的 classes 以了解为什么他们的行为如此不同,因为它们是从相同的 class 派生的。我实际上会研究 A 和 B 基于它们的接口从 X 派生是否有意义。
我也不会推荐 dynamic_cast(ing) 而不通知某人这是更危险的演员表演之一,通常不推荐。
只有当 class 是特定派生的 class 时,我才会做某事。那就是我有:
class X{
int id;
}
class A: public X{
void run();
}
class B: public X{
int lala;
}
我想做一些事情:
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for(int i = 0; i<types.size(); i++){
if(types[i].isType(A)) {types[i].run();}
}
for(int i = 0; i<types.size(); i++){
if(types[i].isType(B)) {var = lala;}
}
}
我不希望 class B 有任何等同于 运行() 的东西,我也不希望 class A 有等同于 lala 的东西。
我知道 Fortran 有一个解决方法
select type ( x => var )
class is ( A )
x.run()
end select
但我不确定我在 C++ 中的选项是什么。
谢谢
您可以使用 dynamic_cast
检查基础 class 指针是否可转换为派生实例。
另一种选择是使用 returns class 的 typeinfo
的虚函数,然后使用该信息将指针转换为可转换类型。根据 dynamic_cast
的实施方式,此 可能 性能更高。因此,如果您想尝试查看此方法在您的平台上是否更快,则可以使用它。
正如@Jarod42 指出的那样,您需要有一个虚函数,在这种情况下是析构函数,dynamic_cast
才能工作。此外,您只需要一个虚拟析构函数来避免删除实例时出现未定义的行为。
例子
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
struct A {
virtual ~A() {
}
virtual const std::type_info& getTypeInfo() const {
return typeid(A);
}
};
struct B : public A {
virtual const std::type_info& getTypeInfo() const override {
return typeid(B);
}
};
struct C : public A {
virtual const std::type_info& getTypeInfo() const override {
return typeid(C);
}
};
int main()
{
std::vector<A*> data;
data.push_back(new A);
data.push_back(new B);
data.push_back(new C);
for (auto& val : data) {
if (val->getTypeInfo() == typeid(A)) {
std::cout << "A";
}
else if (val->getTypeInfo() == typeid(B)) {
std::cout << "B";
}
else if (val->getTypeInfo() == typeid(C)) {
std::cout << "C";
}
std::cout << std::endl;
}
for (auto& val : data) {
delete val;
}
}
您正在寻找 dynamic_cast
.
#include <vector>
using namespace std;
class X {
public:
int id;
virtual ~X() = default;
};
class A : public X {
public:
void run() {}
};
class B : public X {
public:
int lala;
};
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for(int i = 0; i<types.size(); i++){
if (auto ta = dynamic_cast<A *>(types[i])) {
ta->run();
}
}
for(int i = 0; i<types.size(); i++){
if (auto tb = dynamic_cast<B *>(types[i])) {
var = tb->lala;
}
}
}
也可以在此处查看实际效果:https://onlinegdb.com/B1d29P5if。
我不得不修复代码的其他一些问题。由于它们不是你问题的一部分,我不会在这里澄清,但欢迎你提出任何不清楚的问题。
编辑:上面的解决方案有内存泄漏,我没有修复,因为问题不需要它。为了完整起见,这里是修复了内存泄漏的主要函数 (https://onlinegdb.com/ByeOmu9iz):
int main() {
vector<unique_ptr<X>> types;
types.emplace_back(new A);
types.emplace_back(new B);
int var = 0;
for(int i = 0; i < types.size(); ++i) {
if (auto ta = dynamic_cast<A *>(types[i].get())) {
ta->run();
}
}
for(int i = 0; i < types.size(); ++i) {
if (auto tb = dynamic_cast<B *>(types[i].get())) {
var = tb->lala;
}
}
}
请注意,这是一个 C++11 解决方案。
如果您使用的是更旧的编译器,则必须像在原始解决方案中一样继续使用普通指针,并在最后通过对每个元素调用 delete
手动释放内存向量。 (并希望在您到达该步骤之前没有任何异常抛出。)
您还必须将 auto ta
替换为 A* ta
,将 auto tb
替换为 B* tb
。
这个问题的现代 C++17 解决方案是使用变体向量,即 std::vector<std::variant<A, B>>
。为此,您需要一个现代编译器。
这是一个完整的示例,基于 std::variant
documentation:
#include <vector>
#include <variant>
#include <iostream>
class X {
int id;
};
class A: public X {
public:
void run() {
std::cout << "run\n"; // just for demonstration purposes
}
};
class B: public X {
public:
B(int lala) : lala(lala) {} // just for demonstration purposes
int lala;
};
int main() {
std::vector<std::variant<A, B>> types;
types.push_back(A()); // no more new!
types.push_back(B(123)); // no more new!
int var = 0;
for (auto&& type : types) {
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, A>) {
arg.run();
} else {
var = arg.lala;
}
}, type);
}
std::cout << var << '\n'; // just for demonstration purposes
}
作为一个不错的奖励,这个解决方案优雅地摆脱了动态分配(不再有内存泄漏,不需要智能指针)。
我有两个想法....
为什么不使用一个共享方法 return 作为一个值来提供关于它是 A 还是 B 的上下文?例如,如果预计 lala return 的值仅为 0 或更大,则您可以使用 void 运行() 而不是 int 运行() 和 return -1次。
class X {
int id;
virtual int run() = 0; //Assuming X isn't meant to be instantiated
}
class A: public X {
// Return -1 to differentiate between As and Bs
int run() { return -1; }
}
class B: public X {
int lala;
int run() { return lala;}
}
那么你有...
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0, temp = 0;
for( int i = 0; i<types.size(); i++ ) {
if( (temp = types[i].run()) != -1 )
var = temp;
....
}
}
同样,仅当 lala 永远不会期望 return 特定范围的值时才有效。
您还可以在创建 A 或 B 时在 X 中隐藏信息,以跟踪您拥有的内容。
class X {
int id;
bool isA;
}
class A: public X {
A() : isA(true) { };
void run();
}
class B: public X {
B() : isA(false) { } ;
int lala;
}
那么你有...
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for( int i = 0; i<types.size(); i++ ) {
if( types[i].isA == true ) {
types[i].run();
}
else {
var = types[i].lala;
}
}
当然,如果您希望添加 C、D、E...,它就不再值得了,但对于只有两个派生的 classes 来说,还算不错。
我会基于这样一个事实来证明这一点:用户已经不得不查看派生的 classes 以了解为什么他们的行为如此不同,因为它们是从相同的 class 派生的。我实际上会研究 A 和 B 基于它们的接口从 X 派生是否有意义。
我也不会推荐 dynamic_cast(ing) 而不通知某人这是更危险的演员表演之一,通常不推荐。