C++指针协变

标签 c++ pointers covariance

我从来没有想到 c++ 有指针协变,因此你可以像这样开枪打自己的腿:

struct Base
{
    Base() : a(5) {}
    int a;     
};

struct Child1 : public Base
{
    Child1() : b(7) {}
    int b;
    int bar() { return b;}
};

struct Child2 : public Base
{
    Child2(): c(8) {}
    int c;
};

int main()
{
    Child1 children1[2];

    Base * b = children1;

    Child2 child2;

    b[1] = child2; // <------- now the first element of Child1 array was assigned a value of type Child2

    std::cout << children1[0].bar() <<  children1[1].bar(); // prints 57 
}

这是未定义的行为吗?有什么方法可以防止它或至少有来自编译器的警告?

最佳答案

是的,这是未定义的行为。

不,典型的 C++ 编译器目前不太可能在这里识别出值得诊断的东西。但是,C++ 编译器一年比一年聪明。谁知道几年后会发生什么事...

然而,一个小问题:

b[1] = child2; // <------- now the first element of Child1 array was assigned...

没有。这不是第一要素。这是第二个元素。 b[0] 将是第一个元素。此外,b 不是一个数组,它是一个指针。它是指向单个元素的指针。它不是指向二元素数组的指针。

这就是未定义行为的来源。

它不是数组的原因是:

Base * b = children1;

children1 衰减为 Child1 *。如果事情到此结束,您可以说 b 将是一个指向双元素数组的指针。

但这不是事情结束的地方。然后将衰减的指针转换为 Base *。您可以将指向子类的指针隐式转换为指向父类(super class)的指针。但是(现在笼统地说)您不能将指向子类数组的指针转换为父类(super class)数组。因此,b 严格来说是指向单个元素的指针,而 b[1] 成为未定义的行为。

关于C++指针协变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38165811/

相关文章:

c++ - boost::ptr_vector 是如何深拷贝底层对象的?

c++ - 如何使函数指针常量

c++ - 程序在 visual studio 中启动时看不到文件

javascript指针数组或#define缩写

java - 使用泛型的 Map.put

Java协变数组不好吗?

c++ - 用 std::array 替换 []

c++ - C++ 中使用 char* 的指针算术

c - 如何在此 C 函数中将任何内容分配给数组?

c#-4.0 - 如何将 IList<SomeObject> 转换为 IList<ISomeInterface>,其中 SomeObject 使用 C# 4.0 中的协方差实现 ISomeInterface