如何正确分配动态一维数组作为类的成员?
Posted
技术标签:
【中文标题】如何正确分配动态一维数组作为类的成员?【英文标题】:How to properly allocate a dynamic one-dimensional array as a member of a class? 【发布时间】:2021-08-04 00:59:03 【问题描述】:到目前为止,这是我的课程:
template <typename T>
class myClass
private:
size_t capacity_;
size_t size_;
T* the_array;
public:
myClass(size_t cap)
capacity_ = cap;
size_ = 0;
the_array = new T[capacity_];
~myClass() clear();
virtual size_t capacity() const
return capacity_;
virtual size_t size() const
return size_;
virtual bool clear()
delete[] the_array;
return true;
;
我试图在构造函数中分配数组,但是当我创建一个 myClass 对象时,它的 the_array 不是具有我请求的容量的数组。调试时,the_array 只是一个指向 T 的指针。我不确定问题出在构造函数中还是在我为指向 the_array 的指针的私有变量的声明中。
【问题讨论】:
如果你想避免头痛,可以使用std::vector 顺便说一句,你有virtual
成员函数但没有virtual
析构函数,这通常是一个不好的方法。我建议在这里删除virtual
,或者如果您确实需要动态调度,请将析构函数标记为virtual
。
Handy extra reading。 TL;DR 版本,如果你有析构函数,你可能需要一个复制构造函数和赋值运算符来配合它。
“the_array 只是一个指向 T 的指针”——我要告诉你一个大秘密:在 C++ 中没有数组这样的东西。这是一个海市蜃楼,一个幻觉,一个花招。数组实际上只是指向数组第一个值的指针。您的调试器只是向您展示了真实的、红色的状态。显示代码的唯一问题是clear()
应该清除已删除数组的指向nullptr
的指针,并且该类需要一个复制构造函数和一个赋值运算符。
查找“C++ 三规则”。要点是,如果您的类直接管理需要非平凡破坏(即释放资源)的资源(在您的情况下为内存),那么您的类还需要一个复制构造函数和一个赋值运算符。在 C++11 及更高版本中,三规则(通常)变为五规则。
【参考方案1】:
当我创建一个 myClass 对象时,它的 the_array 不是具有我要求的容量的数组。
是的,是的。好吧,无论如何,指向这样一个数组的指针。
调试时,the_array 只是一个指向 T 的指针。
它是指向动态分配数组的指针(第一个元素),该数组包含T
类型的元素。
我不确定问题出在构造函数中还是在我为指向 the_array 的指针的私有变量的声明中。
两者都没有。问题在于你对现实的理解。
new[]
分配一个数组来保存指定类型的指定数量的元素,然后返回一个指向该内存的指针。另一方面,调试器无法知道指针指向的内存类型。它所知道的只是指针指向取消引用类型的实例,因此默认情况下它只能显示该单个实例的数据。但是你知道指针实际上指向的是一个数组,所以你需要明确地告诉调试器这个事实,以便它可以显示数组中的其他元素。大多数调试器都提供一个命令,让您指定在所指向的内存地址中存在多少数组元素。
您显示的代码正确地创建了数组。该代码只是在之后管理数组的方式不完整,因为它不遵循Rule of 3/5/0。它需要实现(或禁用)复制构造函数和复制赋值运算符。对于 C++11 及更高版本,它还应实现移动构造函数和移动赋值运算符。例如:
#include <utility>
#include <algorithm>
template <typename T>
class myClass
private:
size_t capacity_;
size_t size_;
T* array_;
public:
myClass(size_t cap = 0)
capacity_ = cap;
size_ = 0;
array_ = new T[capacity_];
myClass(const myClass& src)
capacity_ = src.capacity_;
size_ = src.size_;
array_ = new T[capacity_];
std::copy_n(src.array_, size_, array_);
myClass(myClass&& src)
capacity_ = std::exchange(src.capacity_, 0);
size_ = std::exchange(src.size_, 0);
array_ = std::exchange(src.array_, nullptr);
~myClass() clear();
myClass& operator=(myClass rhs)
capacity_ = std::exchange(rhs.capacity_, 0);
size_ = std::exchange(rhs.size_, 0);
array_ = std::exchange(rhs.array_, nullptr);
return *this;
size_t capacity() const
return capacity_;
size_t size() const
return size_;
void clear()
delete[] array_;
array_ = nullptr;
capacity_ = size_ = 0;
;
话虽如此,您应该使用std::vector
而不是new[]
,然后所有这些细节都会自动为您处理,因为std::vector
遵循3/5/0 规则,并且编译器-在您的类中生成的构造函数和赋值运算符足以正确管理向量,例如:
#include <vector>
template <typename T>
class myClass
private:
std::vector<T> vec_;
public:
myClass(size_t cap = 0)
vec_.reserve(cap);
size_t capacity() const
return vec_.capacity();
size_t size() const
return vec_.size();
void clear()
vec_.clear();
;
【讨论】:
顺便说一句:vector
-using 版本不需要额外的复制、移动和销毁逻辑,因为编写 vector
的人已经为您完成了所有工作,您的班级将完成默认使用它。从中得出的结论是,您应该在尽可能靠近需要它的资源的地方实现复制移动销毁逻辑,通常添加一个除了复制移动销毁之外什么都不做的额外对象,因为 A) 没有它会更容易以后添加的东西和 B) 它允许以后添加的东西更简单。以上是关于如何正确分配动态一维数组作为类的成员?的主要内容,如果未能解决你的问题,请参考以下文章
分配一维动态数组or 二维动态数组的方法以及学习 new 方法or vector