目录
(1)拷贝赋值运算符 Copy assignment operator
(2)移动赋值运算符 Move assignment operator
1,this指针
this是一个C++内置的特殊指针,用来指向引用当前函数的调用对象。
2,六个默认函数
类中有六个特殊函数(运算符):
Default constructor
Copy constructor
Copy assignment operator
Move constructor (since C++11)
Move assignment operator (since C++11)
Destructor
3个构造函数,2个运算符,1个析构函数。
3,构造函数 Constructor
在创建对象时,构造函数被调用。
构造函数的名字必须和类名相同,没有返回类型(包括void也没有)。
构造函数可以重载。
(1)默认构造函数 Default constructor
如果没有构造函数,那么编译器会自动生成一个默认构造函数。
如果自定义了任何一种构造函数,包括自定义构造函数、自定义拷贝构造函数,自定义移动构造函数,那么都不会自动生成默认构造函数。
#include<iostream>
using namespace std;
class A
{
int x=3;
public:
void out()
{
cout<<x<<" ";
}
};
int main()
{
A a;
a.out();
return 0;
}
ps:默认和自动生成的概念有所差别,尤其是有了delete和default关键字之后更加明显。
(2)自定义构造函数
#include <iostream>
using namespace std;
class MyClass
{
public:
int x,y;
MyClass()
{
x=1,y=2;
}
MyClass(int x)
{
this->x=x,y=2;
}
void out()
{
cout<<x<<y<<endl;
}
};
int main() {
MyClass cs;
cs.out();
MyClass cs2(3);
cs2.out();
return 0;
}
(3)拷贝构造函数 Copy constructor
拷贝构造函数用来创建一个对象,并用另一个对象的数据初始化新建对象。
默认的拷贝构造函数,和用'='完成赋值一样,都是浅拷贝。
要想完成深拷贝,需要自定义拷贝构造函数。
(3.1)正则拷贝构造函数
正则拷贝构造函数的入参只有一个,就是本类的对象
#include <iostream>
using namespace std;
class MyClass
{
public:
int x,y;
MyClass(int x)
{
this->x=x,y=2;
}
MyClass(MyClass &cs)
{
x=cs.x,y=cs.y;
}
void out()
{
cout<<x<<" "<<y<<endl;
}
};
int main() {
MyClass cs(9);
MyClass cs2(cs);
cs2.out();
return 0;
}
必须是引用传递参数,否则会发生无限递归。因为在值传递的过程中,会调用拷贝构造函数。
(3.2)拓展拷贝构造函数
拓展拷贝构造函数的入参除了本类的对象之外,至少还有一个其他参数,
在进行值传递的时候,就不会调用该构造函数,而是直接使用默认的‘=’完成浅拷贝,因为不会发生无限递归情况,所以参数传值或者传引用都可以。
#include <iostream>
using namespace std;
class MyClass
{
public:
int x,y;
MyClass(int x)
{
this->x=x,y=2;
}
MyClass(int t,MyClass cs)
{
x=cs.x+t,y=cs.y+t;
}
void out()
{
cout<<x<<" "<<y<<endl;
}
};
int main() {
MyClass cs(9);
MyClass cs2(1,cs);
cs2.out();
MyClass cs3(cs);
cs3.out();
return 0;
}
输出:
10 3
9 2
本质上,拓展拷贝构造函数其实就是自定义构造函数的一种,一般我们说的拷贝构造函数,就是指正则拷贝构造函数,本文默认同样如此。
如果自定义了正则拷贝构造函数,就不会再生成默认拷贝构造函数,因为签名是相同的。
如果只自定义了拓展拷贝构造函数,那么编译器会生成默认拷贝构造函数。
(4)移动构造函数 Move constructor
#include <iostream>
using namespace std;
class MyClass
{
public:
int x, y;
MyClass(int x)
{
this->x = x, y = 2;
}
MyClass(MyClass &&cs)
{
x = cs.x, y = cs.y;
}
void out()
{
cout << x << " " << y << endl;
}
};
int main() {
MyClass cs(9);
MyClass cs2(std::move(cs));
cs2.out();
return 0;
}
4,赋值运算符
(1)拷贝赋值运算符 Copy assignment operator
#include <iostream>
using namespace std;
class MyClass
{
public:
int x=2,y=3;
MyClass & operator=(const MyClass &cs)
{
x=cs.x+1,y=cs.y+1;
return *this;
}
void out()
{
cout<<x<<" "<<y<<endl;
}
};
int main() {
MyClass cs;
MyClass cs2;
cs2=cs;
cs2.out();
return 0;
}
输出:3 4
这就是重载了等号运算符‘=’,所以如果没有重载,还是会有默认拷贝赋值运算符,就是‘=’,是浅拷贝。
(2)移动赋值运算符 Move assignment operator
#include <iostream>
using namespace std;
class MyClass
{
public:
int x = 2, y = 3;
MyClass & operator=(MyClass &&cs)
{
x = cs.x + 1, y = cs.y + 1;
return *this;
}
void out()
{
cout << x << " " << y << endl;
}
};
int main() {
MyClass cs;
MyClass cs2;
cs2 = std::move(cs);
cs2.out();
return 0;
}
5,析构函数 Destructor
在对象销毁时,析构函数被调用。
如果没有定义析构函数,那么会有一个默认的析构函数。
析构函数的名字必须是类名前加~,没有返回类型和参数。
析构函数不能被重载。
#include <iostream>
using namespace std;
class MyClass
{
public:
int *p;
MyClass(int n)
{
p=new int[n];
}
~MyClass()
{
delete[]p;
}
};
int main() {
MyClass cs(10);
return 0;
}
6,构造函数初始化列表
如果一个类A的数据域中有一个对象B,而且对象B所在类没有无实参构造函数,那么类A的构造函数就必须使用初始化列表。
#include <iostream>
using namespace std;
class MyClass
{
public:
int x,y;
MyClass(int x)
{
this->x=x,y=2;
}
void out()
{
cout<<x<<" "<<y<<endl;
}
};
class MyClass2
{
public:
int *p;
int x;
MyClass mcs;
MyClass2(int n):mcs(n*n),x(55)
{
p=new int[n];
}
~MyClass2()
{
delete[]p;
}
void out()
{
mcs.out();
cout<<x<<endl;
}
};
int main() {
MyClass2 cs(9);
cs.out();
return 0;
}
类成员的初始化顺序,是类中的声明顺序决定的,和成员在初始化列表中的顺序无关,为了避免混淆,最好写成顺序一致。
另外一个示例:
cla.h
class Test
{
public:
Test(int a);
int get();
private:
int testval;
};
class Student {
public:
Student(int a);
Student(Test t);
Test test;
};
cla.cpp
#include"cla.h"
int Test::get()
{
return this->testval;
}
Test::Test(int ina)
{
this->testval = ina+4;
}
Student::Student(int a) : test(a)
{
}
Student::Student(Test t) : test(t)
{
}
main.cpp
#include"cla.h"
#include<iostream>
using namespace std;
int main()
{
Student s1(2);
Test t(3);
Student s2(t);
cout << s1.test.get() << endl;
cout << s2.test.get() << endl;
return 0;
}
输出:
6
7
其中,Student::Student(int a) : test(a) 里面的 test(a)其实调用构造函数
而Student::Student(Test t) : test(t)里面的test(t)其实是调用拷贝构造函数
为什么构造函数初始化列表设计的时候,这里要用对象名test,而不用类名呢?
因为一个类里面可能包含多个成员,都是某个类的对象,用类名就无法区分了。