- 移动构造和移动赋值生成条件
- 移动构造和移动赋值调用逻辑
- 强制生成默认函数的关键字default
- 禁止生成默认函数的关键字delete
移动构造和移动赋值生成条件
C++11中新增的移动构造函数和移动赋值函数的生成条件为:
- 移动构造函数的生成条件:没有自己实现的移动构造函数,并且没有自己实现的析构函数,拷贝构造函数和拷贝赋值函数。
- 移动赋值函数的生成条件:没有自己实现的移动赋值函数,并且没有自己实现的析构函数,拷贝构造函数和拷贝赋值函数。
在这里,移动构造和移动赋值并不是说没有写就会自动生成,而是需要一定的条件支持下才会生成。
当我们实现了移动赋值函数和移动构造函数后,编译器就不会自动生成拷贝构造和拷贝赋值了
移动构造和移动赋值调用逻辑
默认生成的移动构造和移动赋值做的什么赋值
- 默认生成的移动构造函数:对于内置类型来说,完成的为浅拷贝。如果存在自定义类型成员且实现了移动构造函数,这时就会调用自定义类型成员的移动构造函数。
- 默认生成的移动赋值函数:对于内置类型来说,完成的为浅拷贝。如果存在自定义类型成员且实现了移动赋值函数,这时就会调用自定义类型成员的移动赋值函数。
下 面我们模拟实现以下,其中包括自定义string类和person类
namespace test
{
class string
{
public:
//构造函数
string(const char* str = "")
{
_size = strlen(str); //初始时,字符串大小设置为字符串长度
_capacity = _size; //初始时,字符串容量设置为字符串长度
_str = new char[_capacity + 1]; //为存储字符串开辟空间(多开一个用于存放'\0')
strcpy(_str, str); //将C字符串拷贝到已开好的空间
}
//交换两个对象的数据
void swap(string& s)
{
//调用库里的swap
::swap(_str, s._str); //交换两个对象的C字符串
::swap(_size, s._size); //交换两个对象的大小
::swap(_capacity, s._capacity); //交换两个对象的容量
}
//拷贝构造函数(现代写法)
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
cout << "string(const string& s) -- 深拷贝" << endl;
string tmp(s._str); //调用构造函数,构造出一个C字符串为s._str的对象
swap(tmp); //交换这两个对象
}
//移动构造
string(string&& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
cout << "string(string&& s) -- 移动构造" << endl;
swap(s);
}
//拷贝赋值函数(现代写法)
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 深拷贝" << endl;
string tmp(s); //用s拷贝构造出对象tmp
swap(tmp); //交换这两个对象
return *this; //返回左值(支持连续赋值)
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
//析构函数
~string()
{
//delete[] _str; //释放_str指向的空间
_str = nullptr; //及时置空,防止非法访问
_size = 0; //大小置0
_capacity = 0; //容量置0
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
class Person
{
public:
//构造函数
Person(const char* name = "", int age = 0)
:_name(name)
, _age(age)
{}
拷贝构造函数
//Person(const Person& p)
// :_name(p._name)
// , _age(p._age)
//{}
拷贝赋值函数
//Person& operator=(const Person& p)
//{
// if (this != &p)
// {
// _name = p._name;
// _age = p._age;
// }
// return *this;
//}
析构函数
//~Person()
//{}
private:
test::string _name; //姓名
int _age; //年龄
};
}
从以上代码我们可以看出,我们person类中只有一个构造函数,这时是满足我们默认生成的条件的。
int main()
{
test::Person s1("张三", 21);
test::Person s2 = std::move(s1); //想要调用Person默认生成的移动构造
return 0;
}
我们可以看出,此时输出的为移动构造,当我们将person类中的析构拷贝构造等复原的时候,这时就不满足条件了,也就调用的为深度拷贝了
强制生成默认函数的关键字default
在有一些条件下,我们的默认构造总是默认生成失败,为了解决这个问题,C++11推出了关键字default来强制将其生成。
class Person
{
public:
Person() = default; //强制生成默认构造函数
//拷贝构造函数
Person(const Person& p)
:_name(p._name)
, _age(p._age)
{}
private:
cl::string _name; //姓名
int _age; //年龄
};
说明一下: 默认成员函数都可以用default关键字强制生成,包括移动构造和移动赋值。
禁止生成默认函数的关键字delete
当我们想要限制某些默认函数生成时,可以通过如下两种方式:
- 在C++98中:我们可以直接将函数设置为私有,这样外部调用的时候就会直接报错。
- 在C++11中,我们可以在函数声明的后面加上 =delete,表示让编译器不生成该函数的默认版本,我们将=delete修饰的函数称为删除函数。
class CopyBan
{
public:
CopyBan()
{}
private:
CopyBan(const CopyBan&) = delete;
CopyBan& operator=(const CopyBan&) = delete;
};
说明一下: 被=delete修饰的函数可以设置为公有,也可以设置为私有,效果都一样。