• C++11之新的类功能


    新的类功能

    默认成员函数

    原来C++类中,有6个默认成员函数:

    1. 构造函数
    2. 析构函数
    3. 拷贝构造函数
    4. 拷贝赋值重载
    5. 取地址重载
    6. const 取地址重载

    C++11 新增了两个:移动构造函数和移动赋值运算符重载。
    针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

    • 如果你没有自己实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
    • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似)
    • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

    默认生成的移动构造和移动赋值会做什么?

    • 默认生成的移动构造:对于内置类型的成员,编译器会完成值拷贝(浅拷贝),对于自定义类型的成员,如果它实现了移动构造函数,就会调用他的移动构造函数,如果没有实现,就会调用它的拷贝构造函数。
    • 默认生成的移动赋值:对于内置类型的成员,编译器会完成值拷贝(浅拷贝),对于自定义类型的成员,如果它实现了移动赋值,就会调用他的移动赋值,如果没有实现,就会调用它的拷贝赋值。

    下面我们用一个string类和一个Person类来更加深层的理解一下移动构造和移动赋值:

    namespace gtt
    {
    	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;
    			}
    		}
    		void push_back(char ch)
    		{
    			if (_size >= _capacity)
    			{
    				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
    				reserve(newcapacity);
    			}
    			_str[_size] = ch;
    			++_size;
    			_str[_size] = '\0';
    		}
    		string& operator+=(char ch)
    		{
    			push_back(ch);
    			return *this;
    		}
    		const char* c_str() const
    		{
    			return _str;
    		}
    	private:
    		char* _str;
    		size_t _size;
    		size_t _capacity; // 不包含最后做标识的\0
    	};
    }
    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:
    	gtt::string _name;
    	int _age;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140

    虽然Person类并没有实现移动构造和移动赋值,但是拷贝构造,拷贝赋值,析构函数都实现,此时在调用的过程中就不会在默认生成移动构造和移动赋值,我们看下面这段代码:

    int main()
    {
    	Person s1("张三", 18);
    
    	//想要调用Person的移动构造函数
    	Person s2 = move(s1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上述代码中,Person类并没有实现移动构造和移动赋值,此时就会调用我们自己实现的拷贝构造函数,会对我们的s1对象的name成员进行深拷贝,也就会调用string类的拷贝构造函数对name成员进行深拷贝。

    在这里插入图片描述
    但是如果我们将拷贝构造,拷贝赋值,析构函数都注释掉,此时Person类就会默认生成移动构造和移动赋值,就会调用他默认生成的移动构造和移动赋值,对于age这种内置类型就完成值拷贝,对于name这种自定义类型因为我们已经实现了他的移动构造和移动赋值,就会去调用他的移动构造和移动赋值。
    在这里插入图片描述

    类成员变量初始化

    默认生成的构造函数,对于自定义类型的成员会调用其构造函数进行初始化,但并不会对内置类型的成员进行处理。于是C++11支持非静态成员变量在声明时进行初始化赋值,默认生成的构造函数会使用这些缺省值对成员进行初始化。

    class Person
    {
    public:
    	//....
    private:
    	gtt::string _name = "张三";
    	int _age = 21;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。

    class Person
    {
    public:
    	Person(const char* name = "", int age = 0)
    		:_name(name)
    		, _age(age)
    	{}
    
    	//拷贝构造函数
    	Person(const Person& p)
    		:_name(p._name)
    		, _age(p._age)
    	{}
    private:
    	gtt::string _name = "张三";
    	int _age = 21;
    };
    
    int main()
    {
    	Person s1("张三", 18);
    
    	Person s2 = move(s1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    最终结果还是会调用我们移动构造函数。

    再比如我们自己已经写看拷贝构造函数,此时就不会在生成默认构造函数,我们也可以强制生成:

    class Person
    {
    public:
    	//强制生成默认构造函数
    	Person() = default;
    
    	//拷贝构造函数
    	Person(const Person& p)
    		:_name(p._name)
    		, _age(p._age)
    	{}
    private:
    	gtt::string _name = "张三";
    	int _age = 21;
    };
    
    int main()
    {
    	Person s;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

    class Person
    {
    public:
    	Person(const char* name = "", int age = 0)
    		:_name(name)
    		, _age(age)
    	{}
    	Person(Person&& p) = delete;
    private:
    	gtt::string _name = "张三";
    	int _age = 21;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    final与override关键字

    final修饰类

    被final修饰的类叫做最终类,最终类无法被继承:

    class A final
    {
    public:
    	//....
    private:
    	int a = 1;
    };
    
    class B : public A //无法继承A,因为被final修饰
    {
    public:
    	//....
    private:
    	int b = 2;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    final修饰虚函数

    final修饰虚函数,表示该虚函数不能再被重写,如果子类继承后重写了该虚函数则编译报错:

    class A 
    {
    public:
    	virtual void func() final
    	{
    		cout << "hello A" << endl;
    	}
    private:
    	int a = 1;
    };
    
    class B : public A
    {
    public:
    	virtual void func()
    	{
    		cout << "hello B" << endl;
    	}
    private:
    	int b = 2;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    override

    override修饰虚函数

    override修饰子类的虚函数,检查子类是否重写了父类的某个虚函数,如果没有没有重写则编译报错。

    class A
    {
    public:
    	void func()
    	{
    		cout << "hello A" << endl;
    	}
    private:
    	int a = 1;
    };
    
    class B : public A
    {
    public:
    	virtual void func() override
    	{
    		cout << "hello B" << endl;
    	}
    private:
    	int b = 2;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

  • 相关阅读:
    计算机毕业设计Java中华二十四节气文化传承宣展平台(源码+系统+mysql数据库+lw文档)
    经典算法-----农夫过河问题(深度优先搜索)
    循环结构在反汇编中特征
    推荐系统三十六式学习笔记:原理篇.矩阵分解12|如果关注排序效果,那么这个模型可以帮到你
    面试-SpringCloud常见组件和注册表结构+nacos
    多线程(复习)
    JS引擎中的线程,事件循环,上下文
    [数据集][目标检测]胸部解剖检测数据集VOC+YOLO格式100张10类别
    【计组】I/O系统
    Django--29用户权限设计
  • 原文地址:https://blog.csdn.net/2303_77100822/article/details/133297977