【问题标题】:Is it safe to dereference a downcasted derived object via make_unique?通过 make_unique 取消引用向下转换的派生对象是否安全?
【发布时间】:2015-04-05 20:55:24
【问题描述】:

我有一个基类和一个派生类以及一个返回对基类的引用的函数。使用 make_unique 然后向下转换指针是否安全?

我试图避免复制操作。

class Animal {}
class Dog : Animal {}

Animal GetAnimal() { ... }

Dog GetDog() {
   Dog dog = *std::make_unique<Dog>( GetAnimal() );
   return dog;
}

或者有没有更直接的方法?

编辑:

这是实际代码(与我上面显示的非常接近:

// Convert from (m)anaged to (u)nmanaged Title 
Title Data::MarshalTitle(TitleMap ^mdefn) {
    Title udefn = MarshalValue(mdefn);
    return udefn;
}

然后MarshalValue被定义为:

Value Data::MarshalValue(TitleMap ^mdefn)

现在,您在这里看不到 Value 是一个基类,而 Title 是一个派生类。

我从编译器得到的错误是:

error C2440: 'initializing' : cannot convert from 'Definitions::Value' to 'Definitions::Title'  D:\Projects\Parsers\View.cpp

Intellisense 告诉我没有合适的用户定义的从值到标题的转换。

不过,这可以很好地通过编译器,但我不确定这是否安全。

// Convert from (m)anaged to (u)nmanaged Title Dimension definition
Title Data::MarshalTitle(TitleMap ^mdefn) {
    Title udefn = *std::make_unique<Title>(MarshalValue(mdefn));
    return udefn;
}

【问题讨论】:

  • GetAnimal() 按值返回 Animal,所以我看不出如何从中构造 unique_ptrmake_unique 是在这里使用的错误工具。请发布实际编译并说明您的问题的代码。
  • 我认为您有错别字:^ 应该表示 & 吗?和 Data::MarshalValue 在其声明中返回一个值,并在其定义中返回一个标题....
  • 不,^ 是托管类指针引用。此代码位于 C++/CLI 应用程序中。至于 Data::MarshalValue,我没有列出该函数的代码,它返回的是 Value 而不是 Title。返回 Title 的是 MarshalTitle。 MarshalTitle 的全部意义在于它从 Value 的基类转换为 Title 的派生类。转换实际上发生在 Title udefn = *std::make_unique&lt;Title&gt;(MarshalValue(mdefn)); 行中。

标签: c++ c++11 casting c++-cli derived-class


【解决方案1】:

您的问题不是您认为的问题。您很困惑,因为您的代码意味着继承是私有的,相当于以下内容:

class Dog : private Animal {}

有了私有继承,你的狗就不是动物了。

而您确实想要常规继承:

class Dog : public Animal {}

那么你实际上不需要放弃任何东西,因为 C++ 会为你做这件事。

使用公共继承,以下内容无需任何副本即可工作:

void myFun(Animal&);
int main() {
    Dog dog;
    myFun(dog);
}

【讨论】:

  • 我确实将它定义为class Dog : public Animal {}。我在输入时错过了它。但是,如果我不通过 make_unique,我会收到错误:没有合适的用户定义转换。这告诉我它正在寻找一个构造函数并尝试按值复制而不是向下转换。
  • 好的。在 GetDog() 中,您实际上在第一行制作了一个副本。你能提供一段代码来触发你所说的错误吗?
【解决方案2】:

好的,我找到了原始问题的答案。简而言之:这样做是不安全的。它绕过了铸造评估,它会在运行时吹出垫圈。

由于嵌套派生类的数量,实际问题变得模糊不清,其中一个在中间,实际上没有向下转换的构造函数。

实际上,结果是这样的:

class A() {}
class AA() : public A {}
class AAA() : public AA {}
class AAAA() : public AAA {}

这些类中的每一个都有一个类似于以下内容的定义:

class AAAA() : public AAA {
    AAAA();
    virtual ~AAAA()

    // Downcasting constructor
    AAAA( const AAA &base ) : AAA(base) {};
}

然而,在类 AAA() 中,缺少向下转换为 AA 的构造函数,但编译器只能报告它找不到适合 AAAA( AAA ) 的构造函数。

事实上,链条中存在中断,因此它无法达到 AA 或更高级别的任何东西(这是我试图达到的)。

经验教训:在构建深度派生的对象链时,请务必仔细检查以确保每个类都存在向下转换的构造函数,并确保它是公共的。

在另一组不同类的另一个实例中,它从结构转换为类,而 Public: 被遗忘了。在这种情况下,您不会收到无法访问的警告,而只会收到强制转换失败。一切看起来都很好,但链条坏了。

也许这可能会为遇到类似问题的其他人节省时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-02
    相关资源
    最近更新 更多