C++11 -- 类的新功能

news/2024/7/10 20:52:10 标签: c++, 开发语言, 代码规范, 开源

文章目录

  • 类的新功能
    • 默认成员函数
    • 类成员变量初始化
    • 强制生成默认函数的关键字default
    • 禁止生成默认函数的关键字delete
    • 继承和多态中的final和override关键字

类的新功能

默认成员函数

原来在C++类中,有6个默认成员函数:
1: 构造函数
2: 拷贝构造函数
3: 拷贝赋值重载
4: 析构函数
5: 取地址重载
6: const取地址重载

最后重要的是前4个,后两个用处不大,默认成员函数就是我们不写编译器会生成一个默认的,但是C++11中新增了两个默认成员函数:移动构造函数和移动赋值运算符重载.

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

( 1 ) : 如果我们没有实现移动构造函数,且没有实现析构函数,
拷贝构造,拷贝赋值重载中的任意一个(意味着没有实现深拷贝).那么编译器会主动生动生一个默认移动构造.默认生成的移动构造函数:

  • 对于内置类型成员会执行逐成员按字节拷贝,
  • 对于自定义类型成员,如果该成员实现了移动构造就主动调用移动构造.

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

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

( 3 ): 如果我们提供了移动构造和移动赋值,编译器就不会主动提供拷贝构造和拷贝赋值.

如果我们需要对上述结论进行验证,我们则需要使用到以前模拟实现的string类.

namespace yzh
{
	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);
		}
		//构造函数(现代写法)
		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)
		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
	};
}

此外,我们还需要一个简单的Person类,其中Person类中包含着一个自定义成员Date;

class Date
{
  public:
	Date()
		:_a(0)
	{
	}
	Date( const Date& de)
		:_a(de._a)
	{
		cout << "Date中的拷贝构造" << endl;
	}
	/*Date(const Date&& de)
		:_a(de._a)
	{
		cout << "Date中的移动构造" << endl;
	}*/

	Date& operator=(const Date& de)
	{
		_a = de._a;
		cout << "Date中的移动赋值" << endl;
		return *this;
	}
private:
	int _a;
};
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:
	cl::string _name; //姓名
	int _age;         //年龄
};

现在,我们对以上结论验证:
当我们屏蔽Person类中的移动构造时,对于内置类型会完成值拷贝,对于自定义类型

类成员变量初始化

由于默认生成的构造函数,会对自定义成员调用其构造函数进行初始化,但并不会对内置成员进行相关处理(一般为随机值),C++11支持非静态成员在声明时进行初始化,默认生成的构造函数会调用缺省值对内置成员初始化.

class Person
{
public:

private:
	yzh::string _name;
	int _age = 1;
	int _Id = 10163;
	yzh::A _aa;
};

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

C++1可以让我们更好的控制要使用的默认构造函数,如果当我们要默认使用某个默认的成员函数,但是因为一些原因导致这个函数没有默认生成.
例如: 当我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用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)
 {}

 Person(Person&& p) = default;
private:
 bit::string _name;
 int _age;
};
int main()
{
 Person s1;
 Person s2 = s1;
 Person s3 = std::move(s1);
 return 0;
}

我们了解,如果当我们显示写了拷贝构造,编译器就不会默认生成移动构造,可是如果我们在移动构造声明处加上关键字default,编译器就可以默认生成了.
在这里插入图片描述

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

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

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	Person(const Person& p)
		:_name(p._name)
		, _age(p._age)
	{}
	//C++11
	Person(Person&& p) = delete;
private:
	//C++98
	//Person(Person&& p)
	//{}
	yzh::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;               //拷贝构造
	Person s3 = std::move(s1);   //移动赋值
	return 0;
}

继承和多态中的final和override关键字

final修饰基类

如果final修饰基类的话,代表该类无法被继承:

//基类
class Person final  //Person类无法被继承,编译错误.
{
public:
	virtual void Print()
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Print() 
	{
		cout << "hello Student" << endl;
	}
};

final修饰虚函数

如果final修饰虚函数,代表该虚函数不能在子类中重写:

//基类
class Person 
{
public:
	virtual void Print() final
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Print() //重写,编译报错
	{
		cout << "hello Student" << endl;
	}
};

override修饰虚函数

override用于修饰子类的虚函数,主要用于判断子类中是否重写了该虚函数,如果没有重写,则编译报错,所以为了在项目中方便调试,一般在子类的虚函数中加上override.

//基类
class Person 
{
public:
	virtual void Print() 
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Prints() override  
	{
		cout << "hello Student" << endl;
	}
};


http://www.niftyadmin.cn/n/351153.html

相关文章

.Net8顶级技术:边界检查之IR解析(慎入)

前言 C#这种语言之所以号称安全的&#xff0c;面向对象的语言。这个安全两个字可不是瞎叫的哦。因为JIT会检查任何可能超出分配范围的数值&#xff0c;以便使其保持在安全边界内。这里有两个概念&#xff0c;其一边界检查&#xff0c;其二IR解析。后者的生成是前者的功能的保证…

TortoiseSvn和TortoiseGit的安装(超详细带汉化)

前言 TortoiseSvn和TortoiseGit都是针对代码进行版本管理的工具&#xff0c;又俗称小乌龟&#xff0c;简洁而可视化的操作界面&#xff0c;免去繁琐的命令行输入。只需要记住常用的几个操作步骤就能快速上手。工欲善其事必先利其器&#xff0c;我一向奉行的就是复杂的问题简单化…

【算法题】2358. 分组的最大数量

下给大家。点击跳转到网站。](https://www.cbedai.net/binkang ) 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个正整数数组 grades &#xff0c;表示大学中一些学生的成绩。你打算将 所有 学生分为一些 有序 的非空分组&#xf…

CryoEM - 冷冻电镜 CryoSPARC 单颗粒图像数据集构建

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/130822537 CryoSPARC 的 downsample 操作是一种用于减少数据集大小的技术,可以提高计算效率和内存使用率。downsample 操作的原理是将原始图像的分辨率降低,…

SpringMVC详情

JavaEE体系结构包括四层&#xff0c;从上到下分别是应用层、Web层、业务层、持久层。Struts和SpringMVC是Web层的框架&#xff0c;Spring是业务层的框架&#xff0c;Hibernate和MyBatis是持久层的框架。 为什么要使用SpringMVC&#xff1f; 很多应用程序的问题在于处理业务数…

linux 相关常用命令-部署JDK17 Docker Jenkins

配置JDK17 进入目录 cd /usr创建java目录 mkdir javacd /usr/java下载jdk17 wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz解压 tar -zxvf jdk-17_linux-x64_bin.tar.gzvim /etc/profileexport JAVA_HOME/usr/java/jdk-17.0.7 export JRE_HOM…

零基础学习网络安全这一块,请问有哪些相关资料可以推荐一下?

一、相关网站推荐 1、FreeBuf 国内关注度最高的全球互联网安全媒体平台&#xff0c;爱好者们交流与分享安全技术的社区&#xff0c;网络安全行业门户。 2、看雪看雪论坛是个软件安全技术交流场所&#xff0c;为安全技术爱好者提供一个技术交流平台和资源。 3、吾爱破解 吾爱破解…

“三化”引领潮流,时尚代名词

大家好&#xff01;我是微三云小鱼&#xff01; 下面给大家分享一下“潮流文化” 如今越来越多的企业都在向数字化前进 而且也相信数字化代表着 向上、未来还是时尚&#xff0c; 各个企业都希望通过数字化 改变现代管理理念。 希望 像打开云计算一样拓展 另外一个渠道。 除了数…