1. 简介
移动构造函数是C++11中的新特性,它允许对象通过移动而不是复制来传递和初始化。移动构造函数通常用于提高性能,因为它避免了不必要的复制操作,特别是当处理大型对象或使用动态内存分配时。
2. 来源
当拷贝构造函数出现函数返回值 (返回对象)时,代码如下:
#include
#include
using namespace std;
class stu {
public:
string* name = nullptr;
int age;
stu() {
cout "无参构造" endl;
}
// stu() : name(nullptr) {
// cout
// }
stu(const string& n, int a) : name(new string(n)), age(a) {
cout "有参构造" endl;
}
stu(const stu& s) : name(new string(*s.name)), age(s.age) {
cout "拷贝构造" endl;
}
~stu() {
delete name; // 释放堆上分配的内存
name = nullptr;
cout "析构函数" endl;
}
};
stu createstu() {
stu s("华云飞", 240);
return s;
}
int main() {
stu s1 = createstu();
return 0;
}
g++ 编译时运行输出如下:
有参构造
析构函数
MSVC 编译时运行输出如下:
有参构造
拷贝构造
析构函数
析构函数
这种现象是编译器自动优化,编译器有时候为了避免拷贝生成临时对象而消耗内存空间,所以默认会服务器托管网有优化、避免发生过多的拷贝动作所以打印的日志可能不是我们所期望的,这时候,如果手动编译的话,可以添加参数:
#如果手动编译 可以添加以下参数 -fno-elide-constructors
g++ -std=c++11 xxx.cpp -fno-elide-constructors
# 如果使用cmake编译,可以添加配置
CMakeLists.txt 中前面添加:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-elide-constructors")
这时运行结果:
有参构造
拷贝构造
析构函数
拷贝构造
析构函数
析构函数
原因解释:
先创建 对象 s stu s("华云飞", 240)
, 然后执行拷贝构造得到临时对象,接着执行析构函数销毁对象s , 再执行拷贝构造得到对象 s1,接着执行析构函数销毁临时对象,最后再销毁对象 s1,完成整个代码过程。
2.1 tips:
如果编译信息中有如下提示:
cl: 命令行 warning D9002 :忽略未知选项“-fno-elide-constructors”
则:
在 CLion 中使用 cl 编译器时,如果看到类似的警告消息 "warning D9002: ignoring unknown option '-fno-elide-constructors'",这是因为 -fno-elide-constructors 是一个 g++(GCC)编译器选项,而不是 Visual C++(cl)编译器选项。
-fno-elide-constructors 选项用于禁用 C++ 编译器对构造函数进行优化的过程。然而,Visual C++ 的 cl 编译器默认情况下不支持此选项。
如果想在 CLion 中使用 -fno-elide-constructors 选项,需要将项目配置更改为使用 g++ 编译器而不是 cl 编译器。可以按照以下步骤进行更改:
打开 CLion 并导航到 File -> Settings(Windows/Linux)或 CLion -> Preferences(MacOS)。
在设置面板中选择 Build, Execution, Deployment -> Toolchains。
在右侧的 "CMake" 栏中,点击下拉箭头选择已安装的 g++ 工具链。
确保勾选 "Use this toolchain for building" 复选框,并点击 Apply 或 OK 保存更改。
通过这样的配置更改,CLion 将使用 g++ 编译器来构建项目,应该能够正常使用 -fno-elide-constru服务器托管网ctors 选项了。请注意,在切换编译器之后可能需要重新加载项目或重新生成 CMake 配置。
如图:
3. 移动构造
拷贝构造有时会有一些弊端,数据拷贝太多,浪费内存,尤其是一些即将消亡的对象,这些对象销毁了,但是它们的数据还要拷贝出来,我们需要使用。而移动构造让新对象直接接管消亡对象的数据,不用重新开辟空间来拷贝数据。
代码:
#include
#include
using namespace std;
class stu {
public:
string* name = nullptr;
int age;
stu(){
cout "无参构造" endl;
}
// stu() : name(nullptr) {
// cout
// }
stu(const string& n, int a) : name(new string(n)), age(a) {
cout "有参构造" endl;
}
stu(const stu& s) : name(new string(*s.name)), age(s.age) {
cout "拷贝构造" endl;
}
// 移动构造函数 移动构造函数操作的是右值,也就是说,它主要是针对数据来说的。
stu(stu && s){
// 让新对象持有数据的控制权
name = s.name;
age = s.age;
// 让原对象放弃数据的控制权
s.name = nullptr;
s.age = 0;
cout "移动构造" endl;
}
// stu(stu && s):name(s.name),age(s.age){
// s.name = nullptr;
// cout
// }
~stu() {
if(name!= nullptr){
delete name; // 释放堆上分配的内存
name = nullptr;
}
cout "析构函数" endl;
}
};
stu createstu() {
stu s("华云飞", 240);
cout "s: " s.name " " *s.name " " s.age " " &s.age " " &sendl;
return s;
}
int main() {
stu s1 = createstu();
cout "s1: " s1.name " " *s1.name " " s1.age " " &s1.age " " &s1 endl;
return 0;
}
运行结果:
有参构造
s: 000002452CA5FEA0 华云飞 240 000000F4F4F7FC60 000000F4F4F7FC58
移动构造
析构函数
移动构造
析构函数
s1: 000002452CA5FEA0 华云飞 240 000000F4F4F7FD00 000000F4F4F7FCF8
析构函数
先执行有参构造 stu s("华云飞", 240)
生成对象s, 在执行移动构造把 对象s 中的数据的所有权递交给临时对象,同时让原对象放弃数据的控制权
同理 临时对象和 对象s1移动构造也是一样的。
4. std::move 函数
4.1 左值转右值
#include
#include
using namespace std;
int add(int &&a , int && b){
return a + b;
}
int main() {
int a = 20; // a : 左值 , 20 : 右值
int & b = a ; // b : 左值引用 , a:左值
int && c = 30 ; // c :右值引用 , 30 :右值
//把左值变成右值。
int && d = move(a); // d : 右值引用 , a :左值
int num1 = 40 , num2 = 50;
add(move(num1) ,move(num2));
add(60 ,70);
return 0;
}
4.2 对象转换为右值引用
std::move 是一个函数模板,用于将对象转换为右值引用,并且表明该对象的所有权可以被移动。当使用 std::move 将一个对象作为参数传递给其他函数时,意味着放弃了对该对象的所有权,并允许接收函数直接获取并修改其内部状态。这在实现高效的移动语义和避免不必要的拷贝构造/赋值操作时非常有用。
#include
#include
using namespace std;
class stu {
public:
string* name = nullptr;
int age;
stu(){
cout "无参构造" endl;
}
stu(const string& n, int a) : name(new string(n)), age(a) {
cout "有参构造" endl;
}
stu(const stu& s) : name(new string(*s.name)), age(s.age) {
cout "拷贝构造" endl;
}
stu(stu && s){
name = s.name;
age = s.age;
s.name = nullptr;
s.age = 0;
cout "移动构造" endl;
}
~stu() {
if(name!= nullptr){
delete name; // 释放堆上分配的内存
name = nullptr;
}
cout "析构函数" endl;
}
};
int main() {
stu s0;
stu s("华云飞", 240);
cout "s: " s.name " " *s.name " " s.age " " &s.age endl;
stu s2 = move(s);
cout "s2: " s2.name " " *s2.name " " s2.age " " &s2.age endl;
return 0;
}
运行结果:
无参构造
有参构造
s: 00000223323E3F70 华云飞 240 0000007C39CFF880
移动构造
s2: 00000223323E3F70 华云飞 240 0000007C39CFF8D0
析构函数
析构函数
析构函数
5. 总结
C++中的移动构造函数是一种特殊的构造函数,用于在对象被移动时执行高效的资源转移操作。它主要用于提高程序的性能和内存管理效率。
在C++11及以上版本中引入了移动语义,通过右值引用(Rvalue Reference)来实现。移动构造函数使用 && 运算符作为参数类型标识,并且接受一个右值引用参数。通常情况下,它会将传入的参数对象的资源指针拷贝到当前对象,并将原始对象置为空状态,避免进行额外的资源拷贝或分配。
移动构造函数常用于以下场景:
- 在容器类中进行元素插入或重新排序时,可以利用移动语义避免不必要的数据复制。
当使用动态容器(如 std::vector、std::list)存储大量对象时,容器可能会不断地进行内存重新分配和数据复制。在这种情况下,移动构造函数可以通过将对象的所有权从旧容器移动到新容器,避免不必要的数据复制,提高性能。
std::vectorBigObject> CreateBigObjects() {
std::vectorBigObject> objects;
// 添加大量对象到容器中
// ...
return objects; // 移动构造函数被调用,避免了大量的数据复制
}
- 在函数返回值时,可以通过移动语义避免大量数据拷贝操作。
临时对象的传递:当在函数间传递临时对象时,移动构造函数可以避免对临时对象进行深拷贝,提高效率。
void ProcessBigObject(BigObject obj) {
// 处理大对象
}
int main() {
ProcessBigObject(BigObject()); // 临时对象通过移动构造函数传递,避免了深拷贝
return 0;
}
- 在使用智能指针等管理资源的类中,可以通过移动语义进行资源所有权的转移。
动态内存管理:当使用动态分配的内存(如使用 new 运算符分配的内存)来存储对象时,移动构造函数可以在对象的所有权转移后,正确管理内存的释放,避免内存泄漏。
std::unique_ptrBigObject> CreateBigObject() {
std::unique_ptrBigObject> obj = std::make_uniqueBigObject>();
// 对对象进行初始化
// ...
return obj; // 移动构造函数被调用,正确管理内存的释放
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
相关推荐: 【计算机网络】 TCP流量控制——滑动窗口和累积应答
文章目录 累积应答 TCP流量控制——滑动窗口 累积应答 我们前面所说的是我们每发送一个包对端就要回一个ack,那么这样效率太慢了,我们这里就有一个累积应答的机制,就是说我们客户端累积发送多个包,然后服务端再统一进行回复。 TCP流量控制——滑动窗口 那么我们…