Dig*_*Man 8 c++ constructor destructor class
我有以下课程:
class FixedByteStream {
public:
FixedByteStream() : size(0), address(NULL), existing(false) {}
FixedByteStream(int length) : existing(false) {
size = length;
address = new char[length];
}
FixedByteStream(int length, char* addr) : existing(true) {
size = length;
address = addr;
}
FixedByteStream(string* str, bool includeNull = false) : existing(true) {
size = (*str).length();
address = const_cast<char*>((*str).c_str());
if (includeNull){
++size;
}
}
~FixedByteStream() {
if (existing == false) {
delete [] address;
}
address = NULL;
}
int getLength() {
return size;
}
char* getAddressAt(int index) {
return &(address[index]);
}
char& operator[] (const int index) {
return address[index];
}
operator char*() {
return address;
}
private:
bool existing;
int size;
char* address;
};
Run Code Online (Sandbox Code Playgroud)
一个非常简单的测试,能够产生问题:
FixedByteStream received;
received = FixedByteStream(12);
received[0] = 't';
Run Code Online (Sandbox Code Playgroud)
Valgrind警告无效写入,调试已经说明原因.FixedByteStream received;调用没有参数的构造函数(这是一种愚蠢的,因为它不能做任何事情).received = FixedByteStream(12);使用整数参数调用构造函数...然后立即调用析构函数,使对象无效.它仍然可以用于某种原因,但我宁愿它不会被置于如此奇怪的困境中,引发警告.
那么,它为什么被称为那里?我可以在某种程度上理解是否首先调用了析构函数,以摆脱无用的临时对象(不是它需要的),但我几乎无处不在地使用了那种declare-now-assign-later模式而且从未遇到过这样的问题之前.
pmr*_*pmr 10
问题大致如下:
T t; // default constructed t
t = T(2); // T(2) constructor with a single argument, assignment operator= called with this == &t
Run Code Online (Sandbox Code Playgroud)
您不提供赋值运算符,因此临时中的指针值只是复制到t中,然后在临时的析构函数中删除指向的内存.
另外:如果构造的对象无效,则没有默认构造函数.
如果对象具有任何用户定义的构造函数,则始终使用构造函数构造它.仅定义一个没有任何构造函数参数的对象使用默认构造函数,而不管该对象之后是否被覆盖.那是
FixedByteStream received;
Run Code Online (Sandbox Code Playgroud)
将调用默认构造函数.下一行更有趣:
received = FixedByteStream(12);
Run Code Online (Sandbox Code Playgroud)
这一行FixedByteStream用参数创建一个临时的12.在内部,这将分配一些内存,但由于临时表在完整表达式的末尾被破坏(在这种情况下基本上是在分号到达时),你不会做很多好事.一旦构造了这个临时对象,就会将其分配给received使用自动生成的复制赋值,如果您手动编写它,它将看起来像这样:
FixedByteStream& FixedByteStream::operator= (FixedByteStream& other) {
this->existing = other.existing;
this->size = other.size;
this->address = other.address;
return *this;
}
Run Code Online (Sandbox Code Playgroud)
也就是说,一旦执行了这个赋值,就必须使用相同的副本FixedByteStream,其中一个副本即将被销毁,并将释放刚刚分配的资源.这显然不是你想要的,即你肯定需要实现复制赋值运算符以使你的类表现良好.一般来说,析构函数的存在可以做任何有趣的事情,这是一个很好的提示,你也需要一个赋值运算符.实际上,还有另一个生成的操作,即复制构造函数,它大致执行复制赋值所做的操作,只是它复制构造成员而不是分配它们.这也不是你想要的课程.
现在有趣的问题变成了:如何FixedByteStream解决?实际上,您需要使用引用计数来跟踪当前正在查看的对象数FixedByteStream,分配内容的副本,或者您需要使用仅在C中可用的移动语义支持(也称为右值引用) ++ 2011.除非你真的知道自己在做什么,否则我建议你在所有情况下复制流,并为以后留下更高级的方法.
一步步:
//create a new object using the default constructor
//I don't see why you think it's stupid that the constructor is called
//it's doing what you're telling it to do
FixedByteStream received;
//FixedByteStream(12) creates a temporary object
//you then copy this object in the received object you already have
//you're using the default operator =
//the temporary object is deleted after it is copied to received
received = FixedByteStream(12);
//received is a valid object
received[0] = 't';
Run Code Online (Sandbox Code Playgroud)
编辑:
我看到这个问题有很多错误的答案,我会进一步解释。我可能对此有些讨厌,但这是一个非常重要的概念,我投了反对票,因此错误的答案不会被接受并被视为理所当然。
您基本上是在初始化堆栈上的某个对象。
我会简化你的情况:
class A
{
A() {}
A(const A& other) {}
A& operator = (const A& other) {}
};
Run Code Online (Sandbox Code Playgroud)
让我们谈谈范围:
{ //begin scope
A a; //object a is created here
//default constructor is called
} //a is destroyed here
//destructor is called
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好。
现在,分配:
{
//two objects are created with default constructor
A a;
A b;
//object b is assigned to a
//operator = will be called
//both objects are still alive here
a = b;
//...
} // a and b will be destroyed, destructor called
Run Code Online (Sandbox Code Playgroud)
进入最后一部分:
{
A a;
a = A();
}
Run Code Online (Sandbox Code Playgroud)
几乎等同于:
{
A a;
{
A b;
a = b;
}
}
Run Code Online (Sandbox Code Playgroud)
当您调用 时a = A(),A()会创建一个临时对象,该对象被分配a然后销毁。
所以对象b在我的简化中是被破坏的临时对象。不是a,您的原件因此a仍然有效。