考虑下面的示例代码。类Data
用于存储数据数组:为简单起见,我选择使用vector<char>
作为数据成员。但是,这些数据必须由外部例程处理,这需要指向数组的第一个元素的指针,当然还有这个数组的长度:因此,我还实现了函数get_data()
和get_length()
。
class Data
{
public:
Data(const char* sourcePtr, int sourceLength) : _data(sourcePtr, sourcePtr+sourceLength) { ; }
const char* get_data() const { return &_data.front(); }
int get_length() const { return _data.size(); }
void print() const
{
vector<char>::const_iterator it;
for(it = _data.begin(); it != _data.end(); ++it)
cout << *it;
}
private:
vector<char> _data;
};
现在考虑以下代码示例,它使用类Data
。
int main(int argc, char** argv)
{
char* a = new char[4];
a[0] = 'b';
a[1] = 'y';
a[2] = 't';
a[3] = 'e';
Data myData(a,4);
delete[] a;
myData.print();
cout << endl;
const char* aPtr = myData.get_data();
// The following statement frees the memory allocated
// for the data member of the class Data.
delete[] aPtr;
return 0;
}
上述语句delete[] aPtr
释放为类Data
的数据成员分配的内存。因此,当myData
对象自动解除分配时,vector<char>
的内容已经手动释放,显然会出现错误 SIGABRT 。
如何确保编译器将语句delete[] aPtr
作为错误发出信号?您应该如何修改课程Data
以实现此目标?
答案 0 :(得分:6)
解决这个问题最直接的方法是确保谁是指针的“拥有者”。在我的C ++时代,如果一个函数返回一个指针,那么该函数的名称应该以“Create”而不是“Get”开头。之后,我们通常会在这种情况下返回智能指针。
实际上编写C ++并不困难,你永远不需要显式地删除任何指针,而是将它们全部存储在一个智能指针中,当指针被自身销毁时它将被删除。
答案 1 :(得分:4)
停止使用“裸”new
和delete
操作,将new
包装在RAII类型中,除非您正在编写析构函数,否则不要使用delete
RAII类型。
std::unique_ptr<char[]> a{ new char[4] };
a[0] = 'b';
a[1] = 'y';
a[2] = 't';
a[3] = 'e';
Data myData(std::move(a),4);
此代码表示数组的所有权明确赋予智能指针,然后明确转移到Data
对象。尝试delete
数组将无法编译。
这不会阻止用户以后执行delete[] aPtr
但是如果您习惯使用delete
从不,除了在RAII类型的析构函数中,它就会变成明显当有人在他们不应该使用delete
的地方引入错误时,显然,因为他们没有内存。
停止摆弄裸指针。如果您的代码的用户不会停止这样做,那么让他们学习很难。
答案 2 :(得分:1)
你不是。 new
和delete
不跟踪谁拥有指针。这是你的工作。
您可以做的是复制指针指向的数据。那么调用者对其数组的作用并不重要。你拥有自己的副本,并假设你没有做任何像暴露给任何想要它的人那样的脑卒中,那就安全了。
或者,请查看std::unique_ptr
和std::shared_ptr
。 :P就存储器管理而言,它们是新的热点;当你使用智能指针时,内存几乎可以管理自己。
答案 3 :(得分:1)
写好文档。阻止其他程序员做愚蠢的事情不是你的工作。