【发布时间】:2021-08-18 16:10:46
【问题描述】:
fredoverflow(user 237K Rep.) 在他的两个答案中解释了大多数事情
但是在实现 Move 构造函数和重载的 Move 赋值运算符 (OMAO)(我在整个问题中使用这些简短形式)时,我遇到了一些问题,我将在这里提出。
用户 Greg Hewgill (拥有 826K 代表的用户)还有另一个答案。
https://stackoverflow.com/a/3106136/11862989his
我在引用他,
假设你 有一个函数返回一个实质对象然后是一个普通的 C++ 编译器将为 multiply() 的结果创建一个临时对象, 调用复制构造函数初始化 r,然后销毁 临时返回值。 C++0x 中的移动语义允许“移动 构造函数”被调用以通过复制其内容来初始化 r,并且 然后丢弃临时值而不必破坏它。
我也会提到这个问题。
好的,现在开始
代码
.cpp
#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
我在这里放了几个主要函数及其输出,以便我可以轻松地讨论这个问题。
1_main
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());然后 Move 构造函数调用,那么为什么这是怎么回事? - 为什么 make_A() 函数中对象
a的析构函数没有运行?
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,但仍然通过创建临时对象、复制构造函数来完成初始化。 - 我没有得到第二个析构函数正在为哪个对象运行?
3_main
fredoverflow(用户 237K Rep.)保留 Move 重载运算符 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&& 也给出错误[ERROR] can't bind a lvalue to a&&
所以返回类型必须是A,对吗?
4
在 Move 构造函数和 Move 重载 = 运算符中,我使用了 a.s=nullptr; 这个语句总是在 Move 语义中使用 fredoverflow(user) 解释了类似“现在源不再拥有它的对象”但我没有得到它。因为如果我不写这个声明仍然没有问题,一切正常。请解释这一点
【问题讨论】:
-
在您的“移动”构造函数和赋值运算符中,您有声明
a.s=nullptr;。这将导致泄漏,因为分配给a的内存不再被释放。您应该“移动”指针,而不是重新分配和复制(这就是复制构造函数和赋值运算符所做的)。例如只需交换指针:std::swap(s, a.s);(但请记住在构造函数中将s初始化为nullptrfirst)。 -
您想要答案还是答案? ;) 请不要到处使用缩写
-
1_main 移动构造函数由于复制省略未执行。尝试
A a1(std::move(make_A()));或A a1=std::move(make_A());使其显式调用 -
你应该只问你的问题而不用所有的前言和胡扯其他问题和答案
-
每个问题一个问题。此外,第一个“子问题”与What are copy elision and return value optimization? 重复
标签: c++ move destructor move-semantics rvalue-reference