当前位置: 首页 > news >正文

【C++11】类的新功能

目录

默认成员函数

介绍两个关键字:delete 和 default

default:强制生成默认函数的关键字

delete:禁止生成默认成员函数的关键字


默认成员函数

C++11之前只有6个默认成员函数

1. 构造函数

2. 析构函数

3. 拷贝构造函数

4. 拷贝赋值重载

5. 取地址重载

6. const 取地址重载


最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。

C++11 新增了两个:移动构造函数移动赋值运算符重载。

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:   

移动构造

如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个都没实现)。那么编译器会自动生成一个默认移动构造

默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝(浅拷贝)自定义类型成员,则需要看这个成员是否实现移动构造(和拷贝构造相似) 如果实现了就调用移动构造,没有实现就调用拷贝构造。

移动赋值

如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。

默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝自定义类型成员,则需要看这个成员是否实现移动赋值如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似)

如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

注意:如果我们手动实现了移动拷贝或移动赋值,就算我们没有实现拷贝构造和拷贝赋值,编译器也不会去实现默认的拷贝构造和拷贝赋值

我们来看看,默认生成的移动赋值和移动赋值是如何工作的

为了完成测试,我们需要实现一个简化版的string和person类来进行配合完成这次测试

string类:

namespace nxbw
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动构造string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移动拷贝" << endl;swap(s);}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}	private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}

person类:

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:nxbw::string _name;int _age;
};

 情况一:我们实现Person类中的移动构造,移动赋值和析构函数,看Perosn是否会生成默认的移动构造和移动赋值,在主函数mian中,创建一个Person s1,然后将s1转换为右值拷贝构造给s2

int main()
{Person p1("nxbw", 20);Person p2 = move(p1);Person p3;p3 = move(p2);return 0;
}

我们来看看以上代码是否可以调用到默认移动拷贝和移动赋值进行资源的转移

我们打开调试

我们来看看p1的资源是否会转移给p2,p3的资源是否会调用给p1

调试完之后,我们并没有看到资源的转移,而是调用拷贝构造进行的深拷贝,这也间接说明了,只要有拷贝构造,移动构造,析构函数其中任意一个的出现,编译器就不会生成默认的移动构造和移动赋值

情况二:我们将Perosn中的拷贝构造,拷贝赋值和析构函数都注释掉,看看编译器是否会生成默认的移动构造和移动赋值

Person默认生成的移动构造,对于自定义类型来说会去调用它自己的移动构造

对内置类型会进行逐字节拷贝(浅拷贝)

Person默认生成的移动赋值,对自定义类型来说,会调用自己的移动赋值

对内置类型,进行值拷贝

在注释了拷贝构造,拷贝赋值,析构函数之后,经过调试可以发现,Person类中确实生成了默认的移动赋值和移动拷贝

介绍两个关键字:delete 和 default

default:强制生成默认函数的关键字

C++11可以让我们更好的控制要使用的默认成员函数,假设在某些情况下我们需要使用某个默认成员函数,但是因为某些原因导致无法生成这个默认成员函数这时可以使用default关键字强制某个默认成员函数

如下情况:

class Person
{
public:Person(const char* name):_name(name){}
private:nxbw::string _name;
};int main()
{Person p;return 0;
}

实例化一个对象,没有给参数进行构造,编译器会报错,因为我们并没有在Person类中实现默认构造函数,并且我们实现了构造函数,编译器也不会自动生成默认构造函数

这时我们就可以使用default关键字强制生成默认成员函数

class Person
{
public:Person(const char* name):_name(name){}Person() = default;
private:nxbw::string _name;
};int main()
{Person p;return 0;
}

delete:禁止生成默认成员函数的关键字

当我们想要限制某些函数的生成时,可以通过如下两种方式:

C++98中,可以将函数设置为私有,并且只用声明不用定义,这样当外部调用该函数时,就会报错

C++11中,可以加载该函数的声明的后面加上 =delete,意思是让编译器不要生成该函数的默认版本,所以我们将=delete修饰的函数称为删除函数

class Person
{
public:Person() {};Person(const Person&) = delete; //限制默认拷贝构造
private:nxbw::string _name;
};int main()
{Person p1;Person p2(p1);return 0;
}

被=delete修饰的函数可以是公有也可以是私有,效果都一样

看看效果:


http://www.mrgr.cn/news/80204.html

相关文章:

  • Vue组件化编程5:单文件组件
  • 2012年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题-解析版
  • 外卖开发(九)——Excel数据报表ApachePOI
  • react 计算属性
  • 关于在浏览器里面获取手机方向的事件
  • C/C++字符数组与字符串操作
  • JVM系列之内存区域
  • SEGGER | 基于STM32F405 + Keil - RTT组件02 - RTT Viewer替代串口调试,实时打印调试log
  • 【通信网络】二层基础:02 VLAN基础之一
  • 深入理解RSA算法:核心概念与原理详解
  • Linux shell的七大功能 --- history
  • 测试工程师八股文04|计算机网络 和 其他
  • MySQL 存储过程与函数:增强数据库功能
  • Quad Remesher使用教程
  • 区间和并—acwing
  • backtesting.py介绍和相关资料
  • 29.在Vue 3中使用OpenLayers读取WKB数据并显示图形
  • 学习笔记069——Java集合框架
  • 理解数据结构 hashtable的简易理解思路
  • 米哈游前端面试题及参考答案
  • [OpenGL] Transform feedback 介绍以及使用示例
  • More Effective C++之操作符operators
  • gpu硬件架构
  • 《拉依达的嵌入式\驱动面试宝典》—前言目录篇
  • 操作系统内存管理
  • c语言数据结构与算法--简单实现线性表(顺序表+链表)的插入与删除