我有以下课程:
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;
};
这是一个能够产生问题的非常简单的测试:
FixedByteStream received;
received = FixedByteStream(12);
received[0] = 't';
Valgrind警告无效写入,调试显示原因。 FixedByteStream received;
调用不带参数的构造函数(这有点愚蠢,因为它不能执行任何事情)。 received = FixedByteStream(12);
使用整数参数调用构造函数...然后立即调用自身的析构函数,使对象无效。它仍然可以用于某种原因,但我宁愿它不会被置于如此奇怪的困境中,引发警告。
那么,为什么要在那里召唤?我可以稍微了解析构函数是否被称为 first ,以摆脱无用的临时对象(不是它需要的),但我已经实际使用了那种declare-now-assign-later模式无处不在,从未遇到过这样的问题。
答案 0 :(得分:8)
您缺少一个赋值运算符。请记住rule of three(或五个)。
问题大致如下:
T t; // default constructed t
t = T(2); // T(2) constructor with a single argument, assignment operator= called with this == &t
您没有提供赋值运算符,因此临时的指针值只是复制到t中,然后在临时的析构函数中删除指向的内存。
另外:如果构造的对象无效,则没有默认构造函数。
答案 1 :(得分:6)
如果您的对象具有任何用户定义的构造函数,则使用构造函数构造始终。仅定义一个没有任何构造函数参数的对象使用默认构造函数,而不管该对象之后是否被覆盖。那是
FixedByteStream received;
将调用默认构造函数。下一行更有趣:
received = FixedByteStream(12);
此行创建一个带有参数FixedByteStream
的临时12
。在内部,这将分配一些内存,但由于临时表在完整表达式的末尾被破坏(在这种情况下基本上是在分号到达时),你不会做很多好事。构建此临时对象后,使用自动生成的复制分配将其分配给received
,如果您手动编写它,它将看起来像这样:
FixedByteStream& FixedByteStream::operator= (FixedByteStream& other) {
this->existing = other.existing;
this->size = other.size;
this->address = other.address;
return *this;
}
也就是说,一旦执行了这个赋值,就必须使用相同的FixedByteStream
副本,其中一个副本即将被销毁,并将释放刚分配的资源。这显然不是你想要的,即你肯定需要实现复制赋值运算符,以使你的类表现良好。一般来说,析构函数的存在可以做任何有趣的事情,这是一个很好的提示,你也需要一个赋值运算符。实际上,还有另一个生成的操作,即复制构造函数,它大致执行复制赋值所做的操作,只是它复制构造成员而不是分配它们。这也不是你想要的课程。
现在有趣的问题变成:如何解决FixedByteStream
?实际上,您需要使用引用计数来跟踪当前正在查看FixedByteStream
的对象数量,分配内容的副本,或者您需要使用移动语义支持(也称为右值引用)仅适用于C ++ 2011。除非你真的知道自己在做什么,否则我建议你在所有情况下复制流,并为以后留下更高级的方法。
答案 2 :(得分:4)
一步一步:
//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';
编辑:
正如我看到这个问题有很多错误的答案,我会进一步解释。我可能会对此感到讨厌,但这是一个非常重要的概念,我贬低了,所以错误的答案不会被接受并被认可。
你基本上是在堆栈上初始化一些对象。
我会简化你的案子:
class A
{
A() {}
A(const A& other) {}
A& operator = (const A& other) {}
};
我们来谈谈范围:
{ //begin scope
A a; //object a is created here
//default constructor is called
} //a is destroyed here
//destructor is called
到目前为止一切顺利。
现在,分配:
{
//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
到最后一部分:
{
A a;
a = A();
}
几乎相当于:
{
A a;
{
A b;
a = b;
}
}
当您致电a = A()
时,A()
会创建一个临时对象,该对象已分配给a
然后销毁。
因此,简化中的对象b
是被销毁的临时对象。不是a
,原来的a
仍然有效。
答案 3 :(得分:1)
FixedByteStream(12)
通过默认运算符=分配给received
。请注意,您没有使用FixedByteStream(12)
在堆中分配new
,而是在本地范围内分配它,而不指定保存它的变量的名称。
您的代码有点类似于:
FixedByteStream received;
FixedByteStream temp(12);
received = temp;
received[0] = 't';
仅在我的示例temp
中有一个名称,其范围是整个函数,并且在您的测试代码temp
中没有名称,它仅存在一行,然后被销毁
您创建的FixedByteStream(12)
对象无法在此行之后使用,因为它甚至不是命名变量。
答案 4 :(得分:0)
对象已在此行初始化
FixedByteStream received;
在线
received = FixedByteStream(12);
您重新初始化它。正确的方法是:
FixedByteStream received(12);
// Or
FixedByteStream *received;
received = new FixedByteStream(12);
(我绝对会选择第一个)
答案 5 :(得分:-1)
您似乎无法理解对象生命周期,并错误地将此代码解释为Java代码。
当您编写FixedByteStream received;
时,使用无参数构造函数创建FixedByteStream类型的对象。当您编写received = FixedByteStream(12);
时,会创建另一个对象,调用=运算符并解析新创建的对象。
你也没有覆盖operator =所以对象是按字节复制的,这是不正确的。