从基础到派生的 C++ 转换

c++ casting from base to derived

本文关键字:C++ 转换 派生      更新时间:2023-10-16

我从基类访问派生类的值时遇到了一些麻烦。我的代码是:

#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...

谁能帮助我理解:

  1. 有没有更好的方法从基础转换为派生,而不是使用 Get() 方法?
  2. 为什么我没有得到预期的结果?

谢谢

你正在做的不是继承。您正在隐藏成员函数。您正在对象上静态调用函数。

在C++中,如果将基类中的成员声明为 virtual,并且稍后想要重写它,则必须保留其整个签名,包括返回类型。您正在更改返回类型并将其重新声明为 virtual ,这很混乱,但特别是它仅适用于静态调用。Brian 正确地指出,您可以对覆盖的虚函数使用协变返回。然而,其余的仍然是正确的。

这没关系,因为您甚至不使用指针来存储对象,而是将它们静态存储为指针中的Base。您正在分配较大的对象(同样是静态的,您对它有整个迷恋),然后将它们混合在一起形成向量中较小的Base对象,删除其余数据并损坏组合vtable,您是对所有对象的静态调用Base::Get()(而不是"派生"Get),获取指向Base *对象的指针(有点有效),然后向编译器撒谎说它是别的东西, 然后你正在使用你撒谎的记忆。

老实说,我很惊讶它不仅崩溃,您可能是在启用内存保护的情况下以调试模式构建的(如果是 MSVC,则会收到您忽略的运行时警告)。

首先,不要使用结构和公共字段,在需要时使用带有公共方法的类(getter 读取值而无法编辑它们)。

使用虚拟方法,以便您可以通过在指向基类的指针/引用上调用派生类中的方法来调用它们,返回 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);