移动构造函数和移动重载赋值运算符的问题? [英] problems with Move constructor and Move overloaded assignment operator?
问题描述
主要由 fredoverflow(用户 237K 代表)在他的两个答案中解释的所有内容
Mostly all things explained by fredoverflow(user 237K Rep.) in his Two answers
但是在实现移动构造函数和重载移动赋值运算符(OMAO
)(我在整个问题中都使用这些简短形式)时,我遇到了一些问题,我将放在此处.
But while implementing Move constructor and overloaded Move Assignment operator(OMAO
)( I am using these short form throughout the question ) I am facing some problem that I will put here.
用户 Greg Hewgill(拥有 826K 代表的用户)还有另一个答案https://stackoverflow.com/a/3106136/11862989his
我引用他的话,
Also there is another answer by user Greg Hewgill (user with 826K Rep.)
https://stackoverflow.com/a/3106136/11862989his
I am Quoting him,
假设你有一个返回实体对象的函数,然后是一个普通的 C++编译器将为multiply()的结果创建一个临时对象,调用复制构造函数初始化r,然后析构临时返回值.C++0x 中的移动语义允许移动构造器"被调用以通过复制其内容来初始化 r,以及然后丢弃临时值而不必破坏它.
Suppose you have a function that returns a substantial object then an ordinary C++ compiler will create a temporary object for the result of multiply(), call the copy constructor to initialize r, and then destruct the temporary return value. Move semantics in C++0x allow the "move constructor" to be called to initialize r by copying its contents, and then discard the temporary value without having to destruct it.
我也会参考这个问题.
好的,我要开始了
#include"34_3.h"
#include<iostream>
#include<conio.h>
#include<cstring>
A::A() // O arg ctor
{
std::cout<<"0 arg constructor\n";
p=0;
s=nullptr;
}
A::A(int k1,const char *str) // 2 arg ctor
{
std::cout<<"2 arg constructor\n";
p=k1;
s=new char[strlen(str)+1];
strcpy(s,str);
}
A::A(const A &a) // copy ctor
{
std::cout<<"copy constructor\n";
p=a.p;
s=new char[strlen(a.s)+1];
strcpy(s,a.s);
}
A::A(A &&a) // Move ctor
{
std::cout<<"Move constructor\n";
p=a.p;
s=new char[strlen(a.s)+1];
strcpy(s,a.s);
a.s=nullptr;
}
A& A::operator=(const A &a) // Overloaded assignement opeator `OAO`
{
std::cout<<"overloade= operator\n";
p=a.p;
s=new char[strlen(a.s)+1];
strcpy(s,a.s);
return *this;
}
A& A::operator=(A &&a) // `OMAO`
{
std::cout<<"Move overloade = operator\n";
p=a.p;
s=new char[strlen(a.s)+1];
strcpy(s,a.s);
a.s=nullptr;
return *this;
}
A::~A() // Dctor
{
delete []s;
std::cout<<"Destructor\n";
}
void A::display()
{
std::cout<<p<<" "<<s<<"\n";
}
.h
#ifndef header
#define header
struct A
{
private:
int p;
char *s;
public:
A(); // 0 arg ctor
A(int,const char*); // 2 arg ctor
A(const A&); // copy ctor
A(A&&); // Move ctor
A& operator=(const A&); // `OAO`
A& operator=(A&&); // `OMAO`
~A(); // dctor
void display(void);
};
#endif
我将几个主要函数及其输出放在这里,以便我可以轻松地讨论问题.
I am putting few main functions and their outputs here so I can discuss the problem easily.
A make_A();
int main()
{
A a1=make_A();
a1.display();
}
A make_A()
{
A a(2,"bonapart");
return a;
}
输出
2 arg constructor
2 bonapart
Destructor
- 为什么它不执行 Move 构造函数,但是如果我注释掉 .cpp 文件中的 Move 构造函数定义和 .h 文件中的声明,那么它会给出错误
[Error] no matching function for call to 'A::A(A)'
并且如果我使用这个A a1=std::move(make_A());
然后移动构造函数调用,那么为什么会发生这种情况? - 为什么 make_A() 函数中对象
a
的析构函数没有运行?
- why it is not executing Move constructor but if I commented out Move constructor definition in .cpp file and declaration in .h file then it give error
[Error] no matching function for call to 'A::A(A)'
and if I use thisA a1=std::move(make_A());
then Move constructor calls, So why this happening ? - Why destructor for object
a
in make_A() function is not running ?
2_main()
A make_A();
int main()
{
A a1;
a1=make_A();
a1.display();
}
A make_A()
{
A a(2,"bonapart");
return a;
}
输出
0 arg ctor
2 arg ctor
Move overloade = operator
copy ctor
Dctor
Dctor
2 bonapart
Dctor
- 现在这里复制构造函数和析构函数运行由于从 Move 重载 = 运算符函数返回 *this 而创建的临时对象.根据 Greg Hewgill 声明
C++ 0x
允许调用 Move 构造函数以通过复制其内容来初始化,然后丢弃临时值而不必破坏它.我正在使用C++11
但仍然通过创建临时对象、复制构造函数来完成初始化. - 我不知道第二个析构函数正在运行哪个对象?
- Now here copy constructor and destructor runs for temporary object created due to return *this from Move overload = operator function. According to Greg Hewgill statement
C++ 0x
allows Move constructor to be called to initialize by copying it's contents and then discard the temporary value without having to destruct it. I am usingC++11
but still initializing is done by creating temporary object, copy constructor. - I am not getting for which object that 2nd destructor is running?
3_main
fredoverflow(用户 237K 代表)保留了 Move 重载运算符 A&
的返回类型,但我认为这是错误的.
3_main
fredoverflow (user 237K Rep.) kept return type of Move overloaded operators A&
but I think it is wrong.
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&&
也给出错误 [ERROR] 不能将左值绑定到 a&&
so I feel return type should be A&&
or A
but A&&
too give error [ERROR] can't bind a lvalue to a&&
所以返回类型必须是A
,对吗?
so return type must be A
, am I right ?
在 Move 构造函数和 Move 重载 = 操作符中我使用了 as=nullptr;
这个语句总是在 Move 语义中使用 fredoverflow(user) 解释了类似现在源不再拥有它的对象"的内容;但我不明白.因为如果我不写这个语句仍然没有问题一切正常.请解释这一点
In Move constructor and Move overloaded = operator I used a.s=nullptr;
This statement is always used in Move semantics fredoverflow(user) explained something like "now the source no longer owns the object it" but I am not getting it. Because if I did not write this statement still no problem everything works fine. please explain this point
推荐答案
你的班级A
有几个问题:
您的赋值运算符不处理自赋值和泄漏:
Your assignment operator don't handle self assignment and leak:
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;
}
你的动作不是移动而是复制:
Your move doesn't move but copy:
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;
}
由于潜在的复制省略(NRVO),有几种情况(gcc 有标记为 -fno-elide-constructors
来控制)
There are several scenario because of potential copy elision (NRVO)
(gcc has flag as -fno-elide-constructors
to control that)
如果 NRVO 适用,则 a
是构造就地";所以不会发生额外的破坏/移动;
if NRVO apply, then a
is construct "in-place" so no extra destruction/move happens;
否则有一个移动构造函数和a
的销毁.
else there is a move constructor and the destruction of 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-constructors
)
default ctor
ctor(int const char*)
move ctor
destructor
move assignment
destructor
display
destructor
为了
A a1,a2;
a2 = a1 = make_A();
a1 = make_A();
使用移动赋值.a2 = (a1 = make_A())
使用复制赋值作为移动赋值返回(正确)A&
a1 = make_A();
use move assignment.
a2 = (a1 = make_A())
use copy assignment as move assignment returns (correctly) A&
4在 Move 构造函数和 Move 重载 = 操作符中,我使用了 a.s=nullptr;
这个语句总是在 Move 语义中使用 fredoverflow(user) 解释了类似现在源不再拥有它的对象"之类的东西.但我不明白.因为如果我不写这个语句仍然没有问题一切正常.请解释这一点
4 In Move constructor and Move overloaded = operator I used
a.s=nullptr;
This statement is always used in Move semantics fredoverflow(user) explained something like "now the source no longer owns the object it" but I am not getting it. Because if I did not write this statement still no problem everything works fine. please explain this point
你的问题是你复制而不是移动.
Your issue is that you do copy instead of move.
如果你做 s = a.s;
而不是复制
If you do s = a.s;
instead of the copy
s = new char[strlen(a.s) + 1];
strcpy(s, a.s);
那么 this->s
和 as
都指向相同的数据,this
和 a
> 会在它们的析构函数中释放(相同的)内存 ->双重免费错误.
then both this->s
and a.s
would point of same data, and both this
and a
would free the (same) memory in their destructor -> double free error.
a.s = nullptr;
可以解决这个问题.
这篇关于移动构造函数和移动重载赋值运算符的问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!