C++11 -- 类的新功能

news/2024/11/29 8:54:41/

文章目录

  • 类的新功能
    • 默认成员函数
    • 类成员变量初始化
    • 强制生成默认函数的关键字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类中的自定义成员相关结论验证:
对于Person类,在符合默认生成移动构造的条件下,编译器就会默认调用A类中的移动构造和移动赋值.
在这里插入图片描述
注意:
如果我们在A类中显示写了移动赋值,那么编译器就会默认将A类中的拷贝构造为已删除的函数,所以,为了更加方便,最好在A类中额外实现拷贝构造.

  • 如果传的实参为左值,编译器就会主动调用A类中的拷贝构造.
  • 如果传的实参为右值,编译器就会主动调用A类中的移动构造(移动赋值)

在这里插入图片描述

类成员变量初始化

由于默认生成的构造函数,会对自定义成员调用其构造函数进行初始化,但并不会对内置成员进行相关处理(一般为随机值),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++11Person(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.ppmy.cn/news/76482.html

相关文章

Leetcode 538. 把二叉搜索树转换为累加树(Morris 遍历)

Leetcode 538. 把二叉搜索树转换为累加树&#xff08;Morris 遍历&#xff09;题目 给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值等于原树中大于或…

【22-23春】AI作业10-经典卷积网络

1.LeNet & MNIST LeNet是一种神经网络的模型&#xff0c;用于图像识别和分类。他包含 3 个卷积层&#xff0c;2 个池化层&#xff0c;1 个全连接层。其中所有卷积层的所有卷积核都为 5x5&#xff0c;步长 strid1&#xff0c;池化方法都为全局 pooling&#xff0c;激活函数…

05 JDBC基础

什么是持久化 将内存中的数据永久保存在磁盘中,方便以后使用 JDBC是什么 java数据库连接 用于执行sql语句的java API java官方提供接口,各大厂商提供实现类,程序员使用实现类的jar包 JDBC的开发流程 添加包: mysql-connector-java-5.1.48.jar lombok.jar 口诀:贾连欲…

Windows操作系统的文件组织结构和计算方法

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows操作系统的文件组织结构和计算方法。 这是一块非常实用的知识&#xff0c;感谢大家来看这个帖子。 Windows组织结构就是文件的组织形式&#xff0c;其中&#xff1a; 1.Windows逻辑结构…

java+springboot留学生新闻资讯网的设计与实现

Spring框架是Java平台的一个开放源代码的Full-stack(全栈)应用程序框架&#xff0c;和控制翻转容器的实现。Spring框架的一些核心功能理论&#xff0c;可以用于所有Java应用&#xff0c;Spring还为Java EE构建的Web应用提供大量的扩展支持。Spring框架没有实现任何的编程模型&a…

基于脉冲神经网络的物体检测

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 研究的意义在于探索脉冲神经网络在目标检测上的应用&#xff0c;目前主流的脉冲神经网络训练算法有直接BP训练、STDP无监督训练和训练好的ANN的转化&#xff0c;虽然训练算法众多&#xff0c;但是SNN仍然没有一套成熟的训练算…

Apache Pulsar部署搭建

1.部署规划 部署 Pulsar 集群包括以下步骤(按顺序)&#xff1a; 1.部署一个 ZooKeeper 集群&#xff0c;初始化 Pulsar 集群元数据。2.部署一个 Bookeeper 集群。3.部署一个或多个 Pulsar brokers。4.部署 Pulsar manager&#xff08;可选&#xff09;。 2.节点规划 主机名…

AIGC技术研究与应用 ---- 下一代人工智能:新范式!新生产力!(1-简介)

文章大纲 AI GC简介决策式/分析式AI(Discriminant/Analytical AI)和生成式AI (Generative AI)参考文献与学习路径模型进化券商研报陆奇演讲AI GC 《我,机器人》中所演绎的一样,主角曾与机器人展开了激烈的辩论,面对“机器人能写出交响乐吗?”“机器人能把画布变成美丽…