【问题标题】:Downcasting pointer to pointer向下转换指针到指针
【发布时间】:2022-01-18 14:15:25
【问题描述】:

我正在学习 C++ 中的多态性,但我不能将指针向下转换为指针。我有一个类 Base 和一个扩展 Base 的类 Derived。我想使用函数Base **derivedFactory(size_t size) 做一个派生对象池。我尝试做Base** array = new Derived*[size];,但编译器说它不能从 Derived** 转换为 Base**。所以我尝试了下一个:

Base **derivedFactory(size_t size)
{
    Base* array = new Derived[size];
    
    for (size_t idx = 0; idx < size; ++idx)
    {
        Derived derived = Derived();
        array[idx] = derived;
    }
    
    Base** arrayBase = &array;
    
    return arrayBase;
}

然后编译。但是当我想访问所有派生对象时,main.cc 会抛出一个Segmentation fault (core dumped)。它执行 hello(cout) 但在结束循环的第一次迭代之前抛出。

你能帮帮我吗?

Main.cc

#include "main.ih"

int main()
{
    Base **bp = derivedFactory(10);

    for (size_t idx = 0; idx <= 10; ++idx)
    {
        bp[idx]->hello(cout);
        cout << "Not printing\n";
    }
}

类基:

class Base
{
    private:
        virtual void vHello(std::ostream &out)
        {
            out << "Hello from Base\n";
        }
    public:
        void hello(std::ostream &out)
        {
            vHello(out);
        }
};

类派生:

class Derived : public Base
{
    std::string d_text;

    private:       
        void vHello(std::ostream &out) override
        {
            out << d_text << '\n';
        } 

    public:
        Derived()
        {
            d_text = "hello from Derived";
        }
        
        virtual ~Derived()
        {}
};

谢谢!

【问题讨论】:

  • 如果你想要一个“派生池”,这意味着一个派生数组,所以一个指针,因此你应该返回一个Base*而不是Base**。使用这样的原始指针被认为是不好的做法,因为您可能会忘记使用delete 释放资源。另外Derived derived = Derived(); 正在创建一个临时Derived 并将其复制到derived。请改用Derived derived;
  • @Offtkp:“Derived derived = Derived(); 正在创建一个临时 Derived 并将其复制到 derived。”这不是真的。

标签: c++ pointers polymorphism downcast


【解决方案1】:

显示的代码存在多个基本问题。

   Base* array = new Derived[size];

这将创建一个 Derived 对象数组。 C++ 中的这种数组由指向数组中第一个值的指针表示。在这种情况下,这将是一个Derived *,这是您从new 获得的。

在 C++ 中将派生类的指针向下转换为基类的指针是有效的。

但就是这样。这就是你得到的全部。

这并不意味着指向数组中第一个派生对象的指针可以向下转换为指向基类的指针,然后使用指向基类的指针来访问实际数组中的每个派生对象。指针在 C++ 中不能以这种方式工作。试图访问除指针指向的第一个对象之外的任何其他对象是未定义的行为。这是所示代码的第一个基本问题。

Base** arrayBase = &array;

array 是在此函数中声明为本地对象的对象。当这个函数返回时,array 被销毁,就像在这个函数中声明的任何其他变量一样。它将被摧毁。不会再有了。它将不复存在。它会为峡湾而松树。它将成为一个前对象。

显示的代码尝试获取该对象的地址并从该函数返回它。函数返回后,这个指针变成了一个指向被销毁对象的指针,任何访问它的尝试都是未定义的行为。

正确地做到这一点需要完全不同的方法。与其使用new 创建派生对象数组,不如使用new 创建指向基础对象的指针数组。然后,通过newing 一个单独的派生对象来单独初始化每个指针,类似于:

 Base **array = new Base *[size];

 // ...

 array[i] = new Derived{};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-15
    相关资源
    最近更新 更多