C++ 从基础到派生的转换
c++ casting from base to derived
我在从基础 class 访问派生的 class 值时遇到了一些问题。我的代码是:
#include <iostream> // std::cout, std::endl
#include <iomanip> // std::setfill, std::setw
#include <vector>
enum EType
{
E_TYPE_NONE=0,
E_TYPE_BASE,
E_TYPE_DERIVED_1,
E_TYPE_DERIVED_2
};
struct Base
{
Base() : mEType(E_TYPE_BASE), a(0), b(0), c(0) {}
virtual ~Base() {}
virtual Base* Get() {return this;}
EType mEType;
unsigned short a;
unsigned short b;
unsigned short c;
};
struct Derived1 : public Base
{
Derived1() : Base()
{
mEType = E_TYPE_DERIVED_1;
a = 1;
b = 2;
c = 3;
d = 4;
}
virtual ~Derived1() {}
virtual Derived1* Get() {return this;}
unsigned short d;
};
struct Derived2 : public Base
{
Derived2() : Base()
{
mEType = E_TYPE_DERIVED_2;
a = 5;
b = 6;
c = 7;
e = 8;
}
virtual ~Derived2() {}
virtual Derived2* Get() {return this;}
unsigned short e;
};
struct Foo
{
std::vector<Base> mObj;
};
int main(int argc, char* argv[])
{
Foo myCollection;
Derived1 obj1;
Derived2 obj2;
Base obj3;
myCollection.mObj.push_back(obj1);
myCollection.mObj.push_back(obj2);
myCollection.mObj.push_back(obj3);
//=========================================================================
std::cout << "base1 = " << myCollection.mObj[0].a << "; " <<
myCollection.mObj[0].b << "; " <<
myCollection.mObj[0].c << std::endl;
Derived1* current1;
// this way i got access violation when i try to print current1
//current1 = dynamic_cast<Derived1*>(myCollection.mObj[0].Get());
current1 = dynamic_cast<Derived1*>((Derived1*)myCollection.mObj[0].Get());
std::cout << "derived1 = " << current1->a << "; " <<
current1->b << "; " <<
current1->c << "; " <<
current1->d << std::endl;
//=========================================================================
std::cout << "base2 = " << myCollection.mObj[1].a << "; " <<
myCollection.mObj[1].b << "; " <<
myCollection.mObj[1].c << std::endl;
Derived2* current2;
current2 = (Derived2*)myCollection.mObj[1].Get();
std::cout << "derived2 = " << current2->a << "; " <<
current2->b << "; " <<
current2->c << "; " <<
current2->e << std::endl;
//=========================================================================
std::cout << "base3 = " << myCollection.mObj[2].a << "; " <<
myCollection.mObj[2].b << "; " <<
myCollection.mObj[2].c << std::endl;
//=========================================================================
std::cout << "ENTER to exit...";
std::cin.ignore(10000, '\n');
return 0;
}
结果是:
base1 = 1; 2; 3
derived1 = 1; 2; 3; 47472
base2 = 5; 6; 7
derived2 = 5; 6; 7; 47472
base3 = 0; 0; 0
ENTER to exit...
预期结果是:
base1 = 1; 2; 3
derived1 = 1; 2; 3; 4
base2 = 5; 6; 7
derived2 = 5; 6; 7; 8
base3 = 0; 0; 0
ENTER to exit...
谁能帮我理解一下:
- 除了使用 Get() 方法之外,是否有更好的从基类转换为派生类的方法?
- 为什么我没有得到预期的结果?
谢谢
你所做的不是继承。 您正在隐藏成员函数。您正在对您的对象静态调用函数。
在 C++ 中,如果您将基 class 中的成员声明为 virtual
并且稍后想要覆盖它,您必须保留其整个签名,包括 return 类型 。您正在更改 return 类型并将其重新声明为 virtual
,这是一团糟,但特别是它仅适用于静态调用。 Brian 正确地指出您允许对重写的虚函数使用协变 return。然而,其余的仍然是正确的。
没关系,因为您甚至没有使用指针来存储对象,而是将它们静态存储为指针内的 Base
。您正在分配较大的对象(再次静态地,您对它有一个完整的迷信),然后将它们混合在一起成为向量中较小的 Base
对象,丢弃其余数据并破坏组合的 vtable
,你在所有这些上静态调用 Base::Get()
(不是 "derived" Get
s),获取指向 Base *
对象的指针(有点有效)然后说谎告诉编译器它是别的东西,那么你正在使用你撒谎的内存。
老实说,我很惊讶它不仅会崩溃,您可能正在启用内存保护的调试模式下构建(如果是 MSVC,则会收到您忽略的运行时警告)。
首先,不要使用 struct 和 public 字段,在需要的地方使用 class 和 public 方法(getter 读取值而不能编辑它们) .
使用虚拟方法,以便您可以通过在 pointer/reference 上调用它们来调用派生 class 中的方法到基础 class 返回 2 行并再次阅读。
虚方法只要具有相同的签名就可以按预期工作,否则你只需要使用 2 种不同的方法
virtual Derived1* Get() {return this;} //in derived class1
不同于
virtual Base* Get() {return this;}
但你可以做到
virtual Base* Get() {return static_cast<Base*>(this);} //in derived class1
现在您有 2 个具有相同签名的方法。那行得通
Derived1 derived;
Base base;
Base * derivedbase = derived.Get();
if(derivedbase->Get() == derivedbase)
cout<<"Nice eh?"<<endl;
using namespace std;
cout<<derived.d<<endl;
cout<<derived.a<<endl;
cout<<base.a<<endl;
cout<<derivedbase->a<<endl;
//cast to derived
Derived1 *againderived = dynamic_cast<Derived1*>(derivedbase);
cout<<againderived->d<<endl;
//ops that would fail
//Derived2 *wrongcast = dynamic_cast<Derived2*>(derivedbase);
我在从基础 class 访问派生的 class 值时遇到了一些问题。我的代码是:
#include <iostream> // std::cout, std::endl
#include <iomanip> // std::setfill, std::setw
#include <vector>
enum EType
{
E_TYPE_NONE=0,
E_TYPE_BASE,
E_TYPE_DERIVED_1,
E_TYPE_DERIVED_2
};
struct Base
{
Base() : mEType(E_TYPE_BASE), a(0), b(0), c(0) {}
virtual ~Base() {}
virtual Base* Get() {return this;}
EType mEType;
unsigned short a;
unsigned short b;
unsigned short c;
};
struct Derived1 : public Base
{
Derived1() : Base()
{
mEType = E_TYPE_DERIVED_1;
a = 1;
b = 2;
c = 3;
d = 4;
}
virtual ~Derived1() {}
virtual Derived1* Get() {return this;}
unsigned short d;
};
struct Derived2 : public Base
{
Derived2() : Base()
{
mEType = E_TYPE_DERIVED_2;
a = 5;
b = 6;
c = 7;
e = 8;
}
virtual ~Derived2() {}
virtual Derived2* Get() {return this;}
unsigned short e;
};
struct Foo
{
std::vector<Base> mObj;
};
int main(int argc, char* argv[])
{
Foo myCollection;
Derived1 obj1;
Derived2 obj2;
Base obj3;
myCollection.mObj.push_back(obj1);
myCollection.mObj.push_back(obj2);
myCollection.mObj.push_back(obj3);
//=========================================================================
std::cout << "base1 = " << myCollection.mObj[0].a << "; " <<
myCollection.mObj[0].b << "; " <<
myCollection.mObj[0].c << std::endl;
Derived1* current1;
// this way i got access violation when i try to print current1
//current1 = dynamic_cast<Derived1*>(myCollection.mObj[0].Get());
current1 = dynamic_cast<Derived1*>((Derived1*)myCollection.mObj[0].Get());
std::cout << "derived1 = " << current1->a << "; " <<
current1->b << "; " <<
current1->c << "; " <<
current1->d << std::endl;
//=========================================================================
std::cout << "base2 = " << myCollection.mObj[1].a << "; " <<
myCollection.mObj[1].b << "; " <<
myCollection.mObj[1].c << std::endl;
Derived2* current2;
current2 = (Derived2*)myCollection.mObj[1].Get();
std::cout << "derived2 = " << current2->a << "; " <<
current2->b << "; " <<
current2->c << "; " <<
current2->e << std::endl;
//=========================================================================
std::cout << "base3 = " << myCollection.mObj[2].a << "; " <<
myCollection.mObj[2].b << "; " <<
myCollection.mObj[2].c << std::endl;
//=========================================================================
std::cout << "ENTER to exit...";
std::cin.ignore(10000, '\n');
return 0;
}
结果是:
base1 = 1; 2; 3
derived1 = 1; 2; 3; 47472
base2 = 5; 6; 7
derived2 = 5; 6; 7; 47472
base3 = 0; 0; 0
ENTER to exit...
预期结果是:
base1 = 1; 2; 3
derived1 = 1; 2; 3; 4
base2 = 5; 6; 7
derived2 = 5; 6; 7; 8
base3 = 0; 0; 0
ENTER to exit...
谁能帮我理解一下:
- 除了使用 Get() 方法之外,是否有更好的从基类转换为派生类的方法?
- 为什么我没有得到预期的结果?
谢谢
你所做的不是继承。 您正在隐藏成员函数。您正在对您的对象静态调用函数。
在 C++ 中,如果您将基 class 中的成员声明为 Brian 正确地指出您允许对重写的虚函数使用协变 return。然而,其余的仍然是正确的。virtual
并且稍后想要覆盖它,您必须保留其整个签名,包括 return 类型 。您正在更改 return 类型并将其重新声明为 virtual
,这是一团糟,但特别是它仅适用于静态调用。
没关系,因为您甚至没有使用指针来存储对象,而是将它们静态存储为指针内的 Base
。您正在分配较大的对象(再次静态地,您对它有一个完整的迷信),然后将它们混合在一起成为向量中较小的 Base
对象,丢弃其余数据并破坏组合的 vtable
,你在所有这些上静态调用 Base::Get()
(不是 "derived" Get
s),获取指向 Base *
对象的指针(有点有效)然后说谎告诉编译器它是别的东西,那么你正在使用你撒谎的内存。
老实说,我很惊讶它不仅会崩溃,您可能正在启用内存保护的调试模式下构建(如果是 MSVC,则会收到您忽略的运行时警告)。
首先,不要使用 struct 和 public 字段,在需要的地方使用 class 和 public 方法(getter 读取值而不能编辑它们) .
使用虚拟方法,以便您可以通过在 pointer/reference 上调用它们来调用派生 class 中的方法到基础 class 返回 2 行并再次阅读。
虚方法只要具有相同的签名就可以按预期工作,否则你只需要使用 2 种不同的方法
virtual Derived1* Get() {return this;} //in derived class1
不同于
virtual Base* Get() {return this;}
但你可以做到
virtual Base* Get() {return static_cast<Base*>(this);} //in derived class1
现在您有 2 个具有相同签名的方法。那行得通
Derived1 derived;
Base base;
Base * derivedbase = derived.Get();
if(derivedbase->Get() == derivedbase)
cout<<"Nice eh?"<<endl;
using namespace std;
cout<<derived.d<<endl;
cout<<derived.a<<endl;
cout<<base.a<<endl;
cout<<derivedbase->a<<endl;
//cast to derived
Derived1 *againderived = dynamic_cast<Derived1*>(derivedbase);
cout<<againderived->d<<endl;
//ops that would fail
//Derived2 *wrongcast = dynamic_cast<Derived2*>(derivedbase);