C++ 移动构造函数和移动重载赋值运算符存在问题?
fredoverflow(用户237K代表)在他的两个答案中解释了大部分内容C++ 移动构造函数和移动重载赋值运算符存在问题?,c++,move,destructor,move-semantics,rvalue-reference,C++,Move,Destructor,Move Semantics,Rvalue Reference,fredoverflow(用户237K代表)在他的两个答案中解释了大部分内容 但是,在实现Move构造函数和重载Move赋值操作符(OMAO)的过程中(我在整个问题中使用了这些简短的形式),我面临一些问题,我将在这里提出 另外还有一个由用户Greg Hewgill(代表826K的用户)给出的答案 我引用他的话 假设你 有一个函数返回一个重要的对象然后是普通的C++ 编译器将为multiply()的结果创建一个临时对象, 调用复制构造函数初始化r,然后销毁 临时返回值。C++0x中的移动
OMAO
)的过程中(我在整个问题中使用了这些简短的形式),我面临一些问题,我将在这里提出
另外还有一个由用户Greg Hewgill(代表826K的用户)给出的答案
我引用他的话 假设你 有一个函数返回一个重要的对象然后是普通的C++ 编译器将为multiply()的结果创建一个临时对象, 调用复制构造函数初始化r,然后销毁 临时返回值。C++0x中的移动语义允许“移动” 构造函数”,通过复制其内容来初始化r,以及 然后丢弃临时值,而不必销毁它 我亦会就此提出质询 好的,现在我开始 代码 .cpp 我把几个主要函数和它们的输出放在这里,这样我就可以很容易地讨论这个问题 1_main 输出
[error]没有匹配函数来调用'A::A(A)
,如果我使用这个a1=std::Move(make_A())代码>然后移动构造函数调用,那么为什么会发生这种情况呢
a
的析构函数没有运行C++0x
允许调用Move构造函数来初始化它,方法是复制它的内容,然后丢弃临时值而不必销毁它。我使用的是C++11
,但初始化仍然是通过创建临时对象、复制构造函数来完成的A&
,但我认为这是错误的
A make_A();
int main()
{
A a1,a2;
a2=a1=make_A();
a1.display();
a2.display();
}
A make_A()
{
A a(2,"bonapart");
return a;
}
输出
所以我觉得返回类型应该是A&&
或A
,但是A&&
也会给出错误[error]不能将左值绑定到&&
所以返回类型必须是A
,对吗
4.
在Move构造函数和Move重载=运算符中,我使用了
a.s=nullptr
此语句通常用于移动语义,但Overflow(用户)解释了“现在源不再拥有它所拥有的对象”之类的内容,但我没有得到它。因为如果我不写这句话,一切都会好起来的。请解释这一点1\u main:Move构造函数由于复制省略而未执行
交换完成后,观察到额外的析构函数,临时对象被销毁
2_main:与调用移动运算符的1_main中的观察结果相同
3_main:看到错误是因为您使用的是低版本的编译器。可能需要指定-std=c++11
4:a.s=nullptr
不是移动,因为您正在分配新内存并进行某种复制。比如说
A::A(A &&a) // Move ctor
{
std::cout<<"Move constructor\n";
p=a.p;
s=a.s;
a.s=nullptr;
a.p=0;
}
A::A(A&&A)//移动
{
std::cout您的类A
有几个问题:
- 您的分配操作员不处理自分配和泄漏:
A& A::operator=(const A& a)
{
std::cout<<"overload operator=\n";
if (this != &a)
{
p = a.p;
delete[] s;
s = new char[strlen(a.s) + 1];
strcpy(s, a.s);
}
return *this;
}
由于潜在的拷贝省略(NRVO),有几种情况
(gcc有标记为-fno elide构造函数来控制该标记)
如果NRVO适用,则a
是“就地”构造,因此不会发生额外的破坏/移动
否则会有一个移动构造函数和a
的销毁
A make_A()
{
A a(2,"bonapart"); // #2 ctor(int const char*)
return a; // #3 move (elided with NRVO)
} // #4 destruction of a, (elided with NRVO)
int main()
{
A a1; // #1: default ctor
a1 = // #5: Move assignment (done after make_A)
make_A(); // #6: destructor of temporary create by make_A
a1.display();
} // #8: destructor of a1
与NRVO
default ctor
ctor(int const char*)
move assignment
destructor
display
destructor
无NRVO(-fno elide构造函数
)
为了
a1=make_A();
使用移动分配。
a2=(a1=make_A())
使用复制分配作为移动分配返回(正确)A&
四,
在Move constructor和Move-overloaded=operator中,我使用了a.s=nullptr;
此语句总是用于Move语义中,而overflow(用户)解释了类似“现在源不再拥有它所拥有的对象”的内容但我不明白。因为如果我不写这句话,一切都很好。请解释这一点
你的问题是你做的是复制而不是移动
如果您使用s=a.s;
而不是副本
s = new char[strlen(a.s) + 1];
strcpy(s, a.s);
然后this->s
和a.s
将指向相同的数据,并且this
和a
将释放析构函数中的(相同)内存->双重释放错误
a.s=nullptr;
将解决该问题。在“移动”构造函数和赋值运算符中,您有一条语句a.s=nullptr;
。这将导致泄漏,因为为a
分配的内存不再可用。您应该“移动”指针,而不是重新分配和复制(复制构造函数和赋值运算符就是这样做的)。例如,只需交换指针:std::swap(s,a.s);
(但请记住在构造函数中首先将s
初始化为nullptr
),您想要一个新的还是一个答案?;)请不要在所有位置使用缩写1_由于复制省略,主移动构造函数未执行。请尝试a1(std::Move(make_A());
或a1=std::Move(make_A())
要明确地称呼它,你应该在没有序言的情况下提出你的问题,对其他问题含糊其辞,每个问题回答一个问题。还有,第一个“子问题”是一个重复的谢谢,我理解你的答案,这将需要一些时间来理解,我必须学习一些新的点,你在你的答案中提到。我提到这个评论,以了解你,我正在检查答案,但不是忽视。好的。只是一个说明
A make_A();
int main()
{
A a1,a2;
a2=a1=make_A();
a1.display();
a2.display();
}
A make_A()
{
A a(2,"bonapart");
return a;
}
[Error] prototype for 'A& A::operator=(A&&)' does not match any in class 'A'
A::A(A &&a) // Move ctor
{
std::cout<<"Move constructor\n";
p=a.p;
s=a.s;
a.s=nullptr;
a.p=0;
}
A& A::operator=(const A& a)
{
std::cout<<"overload operator=\n";
if (this != &a)
{
p = a.p;
delete[] s;
s = new char[strlen(a.s) + 1];
strcpy(s, a.s);
}
return *this;
}
A::A(A&& a) : p(a.p), s(a.s)
{
a.s = nullptr;
std::cout << "Move constructor\n";
}
A& A::operator=(A&& a)
{
std::cout << "Move overload operator=\n";
if (this != &a) {
p = a.p;
delete [] s;
s = a.s;
a.s = nullptr;
}
return *this;
}
A make_A()
{
A a(2,"bonapart"); // Constructor
return a;
}
A make_A()
{
A a(2,"bonapart"); // #2 ctor(int const char*)
return a; // #3 move (elided with NRVO)
} // #4 destruction of a, (elided with NRVO)
int main()
{
A a1; // #1: default ctor
a1 = // #5: Move assignment (done after make_A)
make_A(); // #6: destructor of temporary create by make_A
a1.display();
} // #8: destructor of a1
default ctor
ctor(int const char*)
move assignment
destructor
display
destructor
default ctor
ctor(int const char*)
move ctor
destructor
move assignment
destructor
display
destructor
A a1,a2;
a2 = a1 = make_A();
s = new char[strlen(a.s) + 1];
strcpy(s, a.s);