派生**到基础**

Posted

技术标签:

【中文标题】派生**到基础**【英文标题】:Derived** to Base** 【发布时间】:2016-10-07 12:38:37 【问题描述】:

考虑以下代码:

#include <iostream>

using namespace std;

class Base

public:
    int foo;
;

class Derived : public Base

public:
    float bar;
;

int main()

    Base** baseArr = new Base*[30];

    for (int i=0; i<30; i++)
    
        Derived* derived = new Derived();
        derived->foo = i;
        derived->bar = i * 2.5;
        baseArr[i] = derived;
    

    //Notice this!
    Derived** derivedArr = (Derived**)(baseArr);

    for (int i=0; i<30; i++)
        cout << "My Base " << i << ": " << derivedArr[i]->foo << ", " << derivedArr[i]->bar << endl;

    return 0;

将这个数组转换为数组转换是否安全?整个程序的指针大小是相同的,所以听起来我不会得到任何填充错误。但是,我知道这样做的正确方法是遍历每个元素并单独进行转换。

但是,我试图利用这种类型的转换通过使用非泛型私有函数返回数组将我的模板公共函数实现移动到 .cpp 文件,所以我可以确定我的 Base 数组将只包含特定的 @ 987654325@指针。

private:
    Base** Find(function<bool(Base*)> Predicate, int& N); //Implemented in .CPP

public:
    template <class T> T** FindByType(int &N) //T is derived from Base
    
        //Safe?
        return (T**)(Find(
            [](Base* b)->bool  return typeid(T) == typeid(b); ,
            N));
    ;

当然,这只是一个简化的示例。在这种情况下,我有很多理由使用 RTTI。 N用于控制数组大小。

我想知道这个不安全的转换是否会因多重继承而失败,例如Derived 类也会继承OtherBase,我想转换为OtherBase**,我也想知道是否如果我决定使用此构造,我可能会遇到未定义的行为,或者我可能遇到的任何潜在问题。

【问题讨论】:

你应该使用标准的containers,比如std::vector 不,这不安全,指针别名说不。 @BasileStarynkevitch,你能在没有 for 循环的情况下安全地将 vector 转换为 vector 吗? @GillBates,有这方面的资源吗? @kityPL:当然不是,从vector&lt;int*&gt; 转换为vector&lt;float*&gt; 通常是不安全的。但是你应该考虑智能指针。真正的 C++11 的经验法则是使用原始指针是一种难闻的气味。有时,您需要这样做,但您应该非常小心。 【参考方案1】:

不,这不安全。

指向Derived 的指针与指向Base 的指针不同。指向Derived 的指针可以转换为指向Base 的指针,但最终结果是不同的指针。

因为指向Derived 的指针与指向Base 的指针不同,指向Derived 的指针也与指向Base 的指针不同。

【讨论】:

它们共享共同的内存大小。我可以确定我的 Base** 数组只包含 Derived* 对象。在许多情况下,您可以将 Base* 转换为 Derived*,尤其是当您确定类型匹配时。 抛开标准不谈,如果不使用虚拟/多重继承不是很安全吗?无论如何,在现代流行的架构上。 @krzaq 这正是我所说的,如果它能让我加快代码速度,我可以坚持一些限制。多重继承不是这里的关键特性。 虚拟或多重继承没有任何区别。数组只包含 Derived * 对象也没关系。 您能解释一下原因吗?我们只是在处理地址时对整数进行操作。我知道这不是我在问题中提到的“标准方式”,但是,我不想迭代 3000 个元素数组只是为了转换指针,并且模板非常好用,而不是在之后手动执行接收 Base* 数组。【参考方案2】:

不,这是不安全的。

如果程序尝试通过以下类型之一以外的 glvalue 访问对象的存储值,则行为未定义:

对象的动态类型, 对象动态类型的 cv 限定版本, 与对象的动态类型类似(如 4.4 中定义)的类型, 对应于对象动态类型的有符号或无符号类型, 一种有符号或无符号类型,对应于对象动态类型的 cv 限定版本, 在其元素或非静态数据成员中包含上述类型之一的聚合或联合类型(递归地包括元素或非静态数据成员 子聚合或包含联合), 一种类型,它是对象动态类型的(可能是 cv 限定的)基类类型, charunsigned char 类型。

无耻地盗取Ben

在将Base** 转换为Derived** 的情况下,这些情况都不成立。

【讨论】:

以上是关于派生**到基础**的主要内容,如果未能解决你的问题,请参考以下文章

C ++从基础到派生的无效转换[重复]

用于将 shared_ptr 向量填充到基础和派生对象的函数模板

C++ 类型将基础对象转换为派生对象

适合具备 C 语言基础的 C++ 教程

派生赋值运算符从基础调用

NET基础:类型基础