0.引言
// 1.拷贝构造函数
Str(const Str& x)//形参必须是引用,最好是const 引用
: val(x.val)//将原有类的val拷贝给新类的val,注意有时候需要深拷贝
{
std::cout<<"Copy constructer is called!"<<std::endl;
}
// 2.移动构造函数
Str(Str&& x)//偷资源的过程就是一个写操作,不能定义为const
: val(x.val), a(std::move(x.a))
{
}
//移动构造函数使用:
Str m;
Str m2 = std::move(m);
// 3.拷贝赋值函数
Str& operator = (const Str& x)
{
std::cout<< "copy assignment is called!"<<std::endl;
val = x.val;//完成拷贝操作
a = x.a;
return *this;//返回一直一般是*this
}
// 4.移动赋值函数
Str& operator = (const Str&& x)
{
std::cout<< "move assignment is called!"<<std::endl;
//首先做一个判断
if(&x == this)//自己给自己赋值
{
std::cout<<"dunmmy assignment"<<std::endl;
return *this;
}
std::cout<<"real assignment"<<lstd::endl;
delete ptr;
ptr = x.ptr;
x.ptr = nullptr;
val = std::move(x.val);//完成拷贝操作
a = std::move(x.a);
return *this;//返回一直一般是*this
}
按行看。
1.构造函数
- 普通构造函数:略。
- 代理构造函数
#include<iostream>
#include<vector>
class Str{
public:
Str()//隐式的返回类型,就是类本身
{
std::cout<< "constructer is called!"<<std::endl;
}
Str(int input){//构造函数重载
x = input;
std<<x<<std::endl;
}
-------------
//代理构造函数
Str Str(int input = 3){
x = input;//具有默认参数
}
Str() : Str(3){
}
void fun(){
std::cout<<x<<std::endl;
}
private:
int x;
};
int main(){
Str m;//constructer is called!
Str n(3);//3
}
#include<iostream>
#include<vector>
class Str{
public:
//代理构造函数
Str() : Str(3){
std::cout<<"here_1<<std::endl;
}
Str(int input){
std::cout<<"here_2"<<std::endl;
x = input;
}
void fun(){
std::cout<<x<<std::endl;
}
private:
int x;
};
int main(){
Str m(30);
m.fun()//30
//here_2 \ here_1 :先执行代理构造函数,再执行原始构造函数
Str n();
n.fun();//3 :代理构造函数
}
- 初始化列表:区分数据成员的初始化与赋值, 通常情况下可以提升系统性能
#include<iostream>
#include<vector>
#include<string>
class Str{
public:
Str(const std::string& val ) : x(val), y(0)//初始化列表
{
std::cout<<"Pre-assignment: "<< x <<std::endl;
std::cout<<"Post-assignment: "<< x <<std::endl;
}
private:
std::string x;
int y;
};
int main(){
Str m("abc");
//Pre-assignment: abc
//Post-assignment: abc
}
- 一些情况下必须使用初始化列表(如类中包含引用成员)
#include<iostream>
#include<vector>
#include<string>
class Str{
public:
//引用必须使用初始化列表进行初始化
Str(const std::string& val,
int& p_i/*这里也必须是引用,不然ref是绑定到形参上的*/ )
: x(val), ref(p_i)
{
std::cout<<"Pre-assignment: "<< x <<std::endl;
std::cout<<"Post-assignment: "<< x <<std::endl;
ref = 3;//这是赋值,也不是初始化,只能使用初始化列表进行初始化
}
private:
std::string x;
int& ref;
};
int main(){
int val;
Str m("abc", val);//合法
stc::cout<<val<<std::endl;//3
}
2.拷贝构造函数
拷贝构造函数:拷贝构造函数的形参必须得是引用类型。但是形参为Str& x引用类型的话,在新类里面就容易对原有类进行更改,所以此时最好的形参类型应该是 ‘const Str& x’ 禁止修改,只读
Str(const Str& x)
: val(x.val)//将原有类的val拷贝给新类的val,注意有时候需要深拷贝
{
std::cout<<"Copy constructer is called!"<<std::endl;
}
如果未显式提供拷贝构造函数,那么编译器会自动合成一个,合成的版本会依次对每个数据成员调用拷贝构造。
Str() = default;//缺省构造函数
Str(const Str&) = default;//缺省拷贝构造函数:不需要具体形参名
3.移动构造函数
移动构造函数 :接收一个当前类右值引用对象的构造函数,移动构造函数:能进一步提升系统的性能。
Str(Str&& x)//偷资源的过程就是一个写操作,不能定义为const
: val(x.val), a(std::move(x.a))
{
}
Str(Str&& x)=default;//缺省移动构造函数
移动构造函数使用:
Str m;
Str m2 = std::move(m);
移动构造函数通常声明为不可抛出异常的函数–noexcept
struct Str2{
//定义了拷贝构造函数,编译器就不会定义缺省拷贝构造函数,
//同时也不会定义缺省移动构造函数
//也不会定义缺省构造函数
Str2(const Str2&){
std::cout<<"Str2 copy constructer is called!"<<std::endl;
}
//移动构造函数是去偷别人的资源,默认是正确的
Str2(Str2&&) noexcept //声明为不可抛出异常的函数
{
std::cout<<"Str2 move constructer is called!"<<std::endl;
}
//人为定义缺省构造函数
Str2() = default;
};
struct Str{
Str() = default;//缺省构造函数
Str(const Str&) = default;//缺省拷贝构造函数:不需要具体形参名
//此时,缺省移动构造函数对m_str2的行为是什么?
缺省移动构造函数,声明为不可抛出异常的函数
Str(Str&& x) noexcept = default;
int val = 3;
std::string a= "abc";
Str2 m_str2;
};
4.拷贝赋值函数( operator = )
#include<iostream>
#include<vector>
#include<string>
struct Str{
Str() = default;
Str(const Str&) = default;
Str(Str&& x) noexcept = default;
int val = 3;
std::string a= "abc";
};
int main(){
Str m;
//调用移动构造
Str m2 = std::move(m);
//调用拷贝构造
Str m2 = m;
//此时是赋值,会调用,拷贝赋值或移动赋值函数
//不是构造,是赋值了!!,此时还没定义拷贝赋值函数,会报错
m2 = m;
}
拷贝赋值函数
#include<iostream>
#include<vector>
#include<string>
struct Str{
Str() = default;
Str(const Str&) = default;
Str(Str&& x) noexcept = default;
- 拷贝赋值函数:返回类型为当前类引用
- operator与=之间可加空格可不加
- 形参的填写,类似于拷贝构造函数
- 函数名就是 operator =
Str& operator = (const Str& x)
{
std::cout<< "copy assignment is called!"<<std::endl;
val = x.val;//完成拷贝操作
a = x.a;
return *this;//返回一直一般是*this
}
int val = 3;
std::string a= "abc";
};
int main(){
Str m;
Str m2;
- 此时是赋值,调用拷贝赋值函数
- copy assignment is called!
- C++ insights查看内部操作为:
- m2.operator = (m);
m2 = m;
------
Str m3;
//拷贝复制函数返回类型为引用,返回的值为*this,
//能实现连等的功能,如果返回值不是引用就没办实现
m3 = m2 = m;
}
5.移动赋值函数 ( operator = )
移动赋值函数
#include<iostream>
#include<vector>
#include<string>
struct Str{
Str() = default;
Str(const Str&) = default;
Str(Str&& x) noexcept = default;
//拷贝赋值函数
Str& operator = (const Str& x)
{
std::cout<< "copy assignment is called!"<<std::endl;
//包含指针
val = x.val;//完成拷贝操作
a = x.a;
return *this;//返回一直一般是*this
}
//移动赋值函数
//this->m; x->m
Str& operator = (const Str&& x)
{
std::cout<< "move assignment is called!"<<std::endl;
//首先做一个判断
if(&x == this)//自己给自己赋值
{
std::cout<<"dunmmy assignment"<<std::endl;
return *this;
}
std::cout<<"real assignment"<<lstd::endl;
delete ptr;
ptr = x.ptr;
x.ptr = nullptr;
val = std::move(x.val);//完成拷贝操作
a = std::move(x.a);
return *this;//返回一直一般是*this
}
int val = 3;
std::string a= "abc";
//假设包含一个指针
int* ptr;
};
int main(){
Str m;
Str m2;
//dunmmy assignment
m = std::move(m);
//real assignment
m = std::move(m2);
}
6.析构函数
略
7.总结
= default
表示使用缺省**函数, = delete
则是禁用默认**函数。通常来说,一个类:
- 如果需要定义析构函数,那么也需要定义拷贝构造与拷贝赋值函数
- 如果需要定义拷贝构造函数,那么也需要定义拷贝赋值函数
- 如果需要定义拷贝构造(赋值)函数,那么也要考虑定义移动构造(赋值)函数
见引言图。