gpt4 book ai didi

c++ - std::unique_ptr 带有派生对象数组,使用已删除函数

转载 作者:IT老高 更新时间:2023-10-28 23:11:05 24 4
gpt4 key购买 nike

在我的数值物理代码中,我需要使用 unique_ptr 创建一个派生对象数组,它们的类型是基类。通常,我会:

// Header file of the Base class
class Particle{
public:
Particle(); // some constructor
virtual ~Particle(); // virtual destructor because of polymorphism
virtual function(); // some random function for demonstration
};

// Header file of the Derived class
class Electron : public Particle{
public:
Electron();
// additional things, dynamic_cast<>s, whatever
};

稍后在我的代码中,要使用 Base 类型指针创建一个 Derived 对象数组,我会这样做

Particle* electrons = new Electron[count];

优点是我能够以非常方便的方式使用数组 electrons[number].function(),因为 [] 中的增量值实际上是指向数组中对象 Electron 正确实例的内存地址。但是,使用原始指针会很麻烦,所以我决定使用智能指针。

问题在于派生对象的定义。我可以做到以下几点:

std::unique_ptr<Particle, std::default_delete<Particle[]>> electrons(new Electron[count]);

创建多态电子数组,甚至使用正确的 delete[] 调用。问题在于调用数组的具体对象的方式,因为我必须这样做:

electrons.get()[number].function();

我不喜欢 get() 部分,一点也不喜欢。

我可以做到以下几点:

std::unique_ptr<Particle[]> particles(new Particle[count]);

是的,在数组中调用 Particle 类型的实例,使用

particles[number].function();

除了我没有使用类 Electron 的具体细节的部分,一切都会很好,花花公子,因此代码是无用的。

现在有趣的部分,让我们再做一件事,好吗?

std::unique_ptr<Particle[]> electrons(new Electron[count]);

轰隆隆!

use of deleted function ‘std::unique_ptr<_Tp [], _Dp>::unique_ptr(_Up*) [with _Up = Electron; <template-
parameter-2-2> = void; _Tp = Particle; _Dp = std::default_delete<Particle []>]’

发生了什么事?

最佳答案

std::unique_ptr正在防止射击自己的脚,如 std::default_delete<T[]>来电delete[] ,具有标准中规定的行为

If a delete-expression begins with a unary :: operator, the deallocation function’s name is looked up in global scope. Otherwise, if the delete-expression is used to deallocate a class object whose static type has a virtual destructor, the deallocation function is the one selected at the point of definition of the dynamic type’s virtual destructor (12.4). 117 Otherwise, if the delete-expression is used to deallocate an object of class T or array thereof, the static and dynamic types of the object shall be identical and the deallocation function’s name is looked up in the scope of T.

换句话说,代码如下:

Base* p = new Derived[50];
delete[] p;

是未定义的行为。

它似乎在某些实现上有效 - 在那里,delete[] call 查找分配的数组的大小并在元素上调用析构函数 - 这要求元素具有众所周知的大小。由于派生对象的大小可能不同,因此指针算法出错,并使用错误的地址调用析构函数。

让我们回顾一下您的尝试:

std::unique_ptr<Particle[]> electrons(new Electron[count]);

std::unique_ptr 中有一个代码的构造函数检测这些违规行为,请参阅 cppreference .

std::unique_ptr<Particle, std::default_delete<Particle[]>> electrons(new Electron[count]);

是未定义的行为,您实际上是告诉编译器 delete[]是释放你推送给 electrons 构造函数的资源的有效方式,这不是真的,如上所述。

...but wait, there is more (priceless comment by @T.C.):

For addition or subtraction, if the expressions P or Q have type “pointer to cv T”, where T and the array element type are not similar ([conv.qual]), the behavior is undefined. [ Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. — end note ]

这意味着不仅删除数组是未定义的行为,索引也是如此!

Base* p = new Derived[50]();
p[10].a_function(); // undefined behaviour

这对你意味着什么?这意味着您不应该多态地使用数组

使用多态的唯一安全方法是使用 std::unique_ptr指向派生对象,如 std::vector<std::unique_ptr<Particle>> (我们那里没有数组的多态使用,但是那里有多态对象的数组)

既然您提到性能至关重要,那么动态分配每个 Particle会很慢 - 在这种情况下,您可以:

  • 使用对象池
  • 利用享元模式
  • 重构它以避免继承
  • 使用 std::vector<Electron>std::unique_ptr<Electron[]>直接。

关于c++ - std::unique_ptr<T[]> 带有派生对象数组,使用已删除函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33461724/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com