我无法从指针到派生类以及从指针到指针到基类进行分配?

I can't assign from pointer to pointer to derived class and pointer to pointer to base class?

本文关键字:指针 基类 分配 派生      更新时间:2023-10-16

可能重复:
衍生**到基础**之间的转换

在学习了几年Python之后,我又回到了C++领域,遇到了一堵强类型墙。我认为我很好地处理了基类和派生类指针之间的基本多态性和类型转换(例如,派生类的指针可以类型转换为其基类的指针吗?),但这里有一个难题:为什么我不能将指向派生类的指标分配给它基类的p-to-p?

为了获得更多的上下文信息(也许还有一些不那么狡猾的技巧),下面是我尝试做的一个简化版本。我想要一个指向对象的指针列表(从一个类派生)和一个标识它们的字符串(即map<string, obj*>)。然后,一组组件将传递一个标识字符串和位置的列表,以存储指向相应对象(即map<string, obj**>)的指针。然后,我应该能够通过字符串id找到合适的对象,并填写合适的指针供组件后续使用。实现这一点的简化代码是

#include <map>
#include <string>
using namespace std;
class base
{
};
class derived: public base
{
};
typedef map<string, base**> BasePtrDict;
typedef map<string, base*> BaseDict;

int main(int argc, char* argv[])
{
    base b1, b2;
    derived d;
    derived* d_ptr;
    BaseDict base_dict;
    base_dict["b1"] = &b1;
    base_dict.insert(make_pair("b2",&b2)); // alternate syntax
    base_dict["d"]= &d;
    BasePtrDict ptr_dict;
    ptr_dict["d"] = &d_ptr;
    for (auto b = ptr_dict.begin(); b != ptr_dict.end(); b++)
        *(b->second) = base_dict[b->first];
    return 0;
}

这在ptr_dict["d"] = &d_ptr;处遇到编译错误。为什么?在C++范式中,我应该做什么?我真的需要到处做难看的(不安全的)reinterpret_cast<base>()吗?

您正在丢失将base *强制转换为derived *所需的信息。

考虑derived从多个基类继承的情况,因此强制转换需要调整指针值。则用于指向基pb的指针的derived *pd = static_cast<derived *>(pb)将自动应用指针调整。

但是derived *pd; base **ppb = &pd; *ppb = *pb将无法应用指针调整,因此这是不合法的。

你应该做的是:

base *b_ptr;
... // existing code
derived *d_ptr = static_cast<derived *>(b_ptr);

考虑如果允许将derived**视为base**:会启用什么

class base
{
};
class derived: public base
{
public:
    int boom;
}

void light_the_fuse( base** pb)
{
    *pb = new base;
}

int main()
{
    derived* pd = NULL;
    light_the_fuse( &pd);
    pd->boom = 42;  // uh oh, `pd` points to a `class base`...
    return 0;
}

为了进行从子到基的转换,需要将类型关联起来。对于derived**base**,这意味着derived*base*的子级,这显然不是真的,因为它们是正交指针类型(恰好指向相关类)。

您应该能够用static_cast而不是reinterpret_cast来解决这个问题:

ptr_dict["d"] = &static_cast<base*&>(d_ptr);

derived*直接转换为base*是可以的,但这不是您要尝试的。您正在尝试执行derived**base**,这不是隐式强制转换。除此之外,我想问一下为什么?我甚至会问你是否真的需要三分球,但需要双分球?

更换会简单得多

derived* d_ptr;

带有

base* d_ptr;

当您可能需要应用dptr的确切类型时,可以随时使用强制转换,但当然,面向对象的原则表明,这种情况应该非常罕见。

不能也不应该将Derived**分配给Base**,因为它不安全。如果强制转换被允许,那么它将允许Derived*指向非Dervied类型的类型。考虑以下内容:

class Base {};
class Derived: public Base {};
class Other: public Base {};
...
Derived derived;
Derived* derived_p   = &derived;   // derived_p points to derived.
Derived** derived_pp = &derived_p; // derived_p points to derived.
                                   // derived_pp points to derived_p.
Base** base_pp = derived_pp;       // derived_p points to derived.
                                   // base_pp points to derived_p.
Other other;
Other* other_p = &other;           // other_p points to other.
*bpp = op;                         // base_pp points to derived_p.
                                   // derived_p points to other.

如果上述代码有效,则允许Derived* derived = new Other()

不幸的是,我不太确定要完成什么,所以我无法提供替代的解决方案。

这几乎类似于"为什么我需要一个reinterpret_cast来将Fred**const转换为void**const?"?

指向两种不同对象类型的指针基本上是不兼容的。但是,也有例外

  • Derived*Base*。。。Derived和Base是相关的,因此允许转换(在类型转换为Base*之后,指针实际上指向Base类型的对象
  • T*void*-允许(参见上面的链接)

但是没有理由允许D**转换为B**,因为B*D*本质上是不同的类型。