系列服务器开发
前言
本文验证主要针对有一定c++基础的同学,如果初学者需要自己补充下知识点,才能更好的理解本篇验证。
一、类中默认的函数?
c++类的默认八种函数
class A
{
public:
A(); // 默认构造函数;
A(const A&); // 默认拷贝构造函数
~A(); // 默认析构函数
A& operator = (const A&); // 默认重载赋值运算符函数
A* operator & (); // 默认重载取址运算符函数
const A* operator & () const; // 默认重载取址运算符const函数
A(A&&); // 默认移动构造函数
A& operator = (const A&&); // 默认重载移动赋值操作符
};
C++11新增标识符,控制这些默认函数是否使用:
default:被标识的默认函数将使用类的默认行为,如:A() = default;
delete:被标识的默认函数将禁用,如:A() = delete;
override:被标识的函数需要强制重写基类虚函数;
final:被标识的函数禁止重写基类虚函数;
什么情况下会触发拷贝构造函数:
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。可以分为三种情况:
① 程序中需要新建立一个对象,并用另一个同类的对象对它初始化,如前面介绍的那样。
② 当函数的参数为类的对象时。在调用函数时需要将实参对象完整地传递给形参,也就是需要建立一个实参的拷贝,这就是按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实参完全相同的值。
③ 函数的返回值是类的对象。在函数调用完毕将返回值带回函数调用处时。此时需要将函数中的对象复制一个临时对象并传给该函数的调用处
注:
如果没有显式定义,编译器会自动生成一个默认的构造函数,默认的构造函什么都不会做,如果定义了带参数的构造函数,就不会生成默认的构造函数,可以通过default来定义默认的,或者delete来禁止。
如下:包括对象a的构造、a析构、a对象的临时对象tmp的拷贝构造与析构
A fun(){
A a;
return a;
}
二、c++知识点介绍
构造函数
构造函数作用是对对象进行初始化,在堆上new一个对象或在栈上定义一个临时对象时,会自动调用对象的构造函数。有初始化列表和构造函数体内赋值两种方式,初始化列表在初始化对象时更高效(每个成员在初始化列表中只能出现一次),减少了一次赋值操作,推荐此方法;
以下成员变量必须在初始化列表中初始化:常量成员变量、引用类型成员变量、没有缺省构造函数的成员变量;
析构函数
析构函数作用是做一些清理工作,delete一个对象或对象生命周期结束时,会自动调用对象的析构函数;
拷贝构造函数
拷贝构造函数实际上是构造函数的重载,具有一般构造函数的所有特性,用此类已有的对象创建一个新的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中。用类的一个已知的对象去初始化该类的另一个对象时,会自动调用对象的拷贝构造函数;
重载赋值运算符函数
它是两个已有对象,一个给另一个赋值的过程。当两个对象之间进行赋值时,会自动调用重载赋值运算符函数,它不同于拷贝构造函数,拷贝构造函数是用已有对象给新生成的对象赋初值的过程;默认的赋值运算符重载函数实现将数据成员逐一赋值的一种浅拷贝,会导致指针悬挂问题。
移动构造函数
重载移动赋值操作符函数
源对象资源的控制权全部交给目标对象,可以将原对象移动到新对象, 用于a初始化b后,就将a析构的情况;
拷贝构造函数的参数是一个左值引用,但是移动构造函数的初值是一个右值引用;
临时对象即将消亡,并且它里面的资源是需要被再利用的,这个时候就可以使用移动构造。移动构造可以减少不必要的复制,带来性能上的提升。
重载取址运算符函数
重载取址运算符const函数
重载取址运算符函数没有参数;如果没有显式定义,编译器会自动生成默认的重载取址运算符函数,函数内部直接return this,一般使用默认即可;
三、验证构造函数、拷贝构造、赋值、移动等调用
c++ 函数验证
#include <iostream>
#include <string>
class A
{
public:
A(){
std::cout<< "构造" <<std::endl;
}
~A(){
std::cout<< "析构" <<std::endl;
}
A(const A &a){
std::cout<< "拷贝构造" <<std::endl;
}
A &operator=(const A &a) {
std::cout<< "赋值构造" <<std::endl;
return *this;
}
A(const A &&a){
std::cout<< "移动构造" <<std::endl;
}
A &operator=(A &&a) {
std::cout<< "移动赋值构造" <<std::endl;
return *this;
}
};
验证:
int main(int argc, char* argv[]) {
//情况1
//A对象中未定义移动构造与移动拷贝时:
A fun(){
A a;
return a;
}
fun();
//构造、拷贝构造、析构、析构
//情况2
//A对象中未定义移动构造与移动拷贝时:
A fun(){
A a;
return a;
}
auto b = fun();
//构造、拷贝构造、析构、拷贝构造、析构、析构
//情况3
//新增class A对象中的移动构造、赋值移动构造:
A fun(){
A a;
return a;
}
auto b = fun();
// 构造、移动构造、析构、移动构造、析构、析构
//情况4
//新增class A对象中的移动构造、赋值移动构造:
A fun(){
A a;
return a;
}
auto &&b = fun();
// 构造、移动构造、析构、析构
//情况5
//新增class A对象中的移动构造、赋值移动构造:
A fun(){
A a;
return a;
}
A b;
b = fun();
//构造、构造、移动构造、析构、移动赋值构造、析构、析构、
//情况6
//新增class A对象中的移动构造、赋值移动构造:
A funa(){
A a;
return std::move(a);
}
auto b = funa();
//构造、移动构造、析构、移动构造、析构、析构
//情况7
//新增class A对象中的移动构造、赋值移动构造:
A funa(){
A a;
return std::move(a);
}
auto &&b = funa();
//构造、移动构造、析构、析构
//情况8
//新增class A对象中的移动构造、赋值移动构造:
A&& funb(){
A a;
return std::move(a);
}
auto b = funb();
//构造、析构、移动构造、析构
//情况9
//新增class A对象中的移动构造、赋值移动构造:
int fune(A a){
auto b = a;
return 0;
}
A a;
fune(a);
//构造、拷贝构造、拷贝构造、析构、析构、析构
//情况10、
//新增class A对象中的移动构造、赋值移动构造:
int func(const A& a){
auto b = a;
return 0;
}
A a;
func(a);
//构造、拷贝构造、析构、析构
//情况11
//新增class A对象中的移动构造、赋值移动构造:
int fund(A&&a){
A b = a;
return 0;
}
A a;
fund(std::move(a));
//构造、拷贝构造、析构、析构
//情况12
//新增class A对象中的移动构造、赋值移动构造:
int fune(A a){
auto b = a;
return 0;
}
A a;
fune(std::move(a));
//构造、移动构造、拷贝构造、析构、析构、析构
//情况12
int fune(A a){
auto b = a;
return 0;
}
A a;
fune(a);
//构造、拷贝构造、拷贝构造、析构、析构、析构
//情况13
//新增class A对象中的移动构造、赋值移动构造:
int funf(A&&a){
A &b = a;
return 0;
}
A a;
funf(std::move(a));
//构造、析构
return 0;
}
注意:
编译时,要禁止编译器优化,否则,看不出效果。
g++ test.cpp -o test -std=c++11 -fno-elide-constructors
总结
通过本文的学习,你应该对c++类对象的默认几种函数调用有了基本的了解。
今天的文章析构方法调用顺序_析构方法调用顺序「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/88398.html