wordpress 打分插件/微信搜一搜seo
文章目录
- 前言
- 一.构造函数中的初始化列表
- 拷贝对象时的一些编译器优化
- 二.static成员
- 三.友元
- 四.内部类
- 总结
前言
前两期我们将类和对象的重点讲的差不多了,这一篇文章主要进行收尾工作将类和对象其他的知识点拉出来梳理一遍,并且补充前两篇没有讲过的小细节。
一、构造函数中的初始化列表
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。
为什么加入了一个const变量就无法引用默认的构造函数了呢?
我们都知道,const成员在定义的时候必须初始化,前面我们介绍过可以加缺省值,但是缺省值也不是初始化,那么到底在哪初始化呢?这就要介绍到构造函数的初始化列表了,初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
class A
{
public://1.哪个对象调用初始化函数,初始化列表是它所有成员变量初始化的地方//2.不管是否显示在初始化列表写,编译器会让每个变量都在初始化列表定义初始化A():_x(1),_a2(1){_a1++;_a2--;}
private:int _a1 = 1; //声明 使用缺省值并不是初始化int _a2 = 2;const int _x ;
};
int main()
{A aa; //对象整体的定义,每个成员什么时候定义呢?return 0;
}
那么大家可以猜一猜a1和a2的值分别是多少呢?当我们给初始化的时候就不用缺省值了,所以在初始化列表中a2被初始化为1,而a1没有初始化所以用了缺省值1,a1++后变成了2,a2--后变成了0,所以答案是2和0.
初始化列表中,const修饰的变量,引用类型和自定义类型(没有默认构造的自定义类型)都必须在初始化列表初始化,不然编译器会报错,并且自定义类型初始化回去调用其自定义类型的构造函数。
class B
{
public:B():_b(1){cout << "B()" << endl;}
private:int _b;
};
class A
{
public://1.哪个对象调用初始化函数,初始化列表是它所有成员变量初始化的地方//2.不管是否显示在初始化列表写,编译器会让每个变量都在初始化列表定义初始化A():_x(1),_a2(1),ret(_a1),_bb(){_a1++;_a2--;}
private:int _a1 = 1; //声明 使用缺省值并不是初始化int _a2 = 2;//有三类变量必须在初始化列表初始化const int _x ;int& ret;B _bb;
};
那么如果自定义类型是带参的构造函数我们该如何初始化呢?
这种带参的我们只需要在括号内传个值即可,编译器会去调用它的构造函数初始化。
注意:初始化列表中每个成员只能出现一次,因为初始化只能一次不能初始化两次。
建议:尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
下面我们看一道题:
class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}
上面这道题答案应该是什么呢?首先我们定义了对象A并且调用构造函数传了1过去,这时候大多数人会认为a1先被初始化为1,然后s2被a1初始化也是1,但其实结果并不是这样。有这样一条规则:成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后顺序无关。也就是说初始化的顺序是看谁先声明的,谁先声明谁就先初始化,所以在上面那道题中a2先声明所以a2先用a1初始化,而这个时候的a1是随机数,所以a2被初始化为随机数,然后a1进行初始化,a1用a初始化所以a1是1,那么答案就是1和随机值。
拷贝对象时的一些编译器优化
下面我们讲一下编译器的优化:
class A
{
public:A(int a):_a1(a){}
private:int _a2;int _a1;
};
int main() {A aa(1);A aa2 = 1;
}
为什么我们能直接用1去初始化aa2呢?要知道1是整形,而aa2是自定义类型,其实这里包含了编译器优化,首先我们要知道这里是隐式类型转换,就像下图:
在这里是直接把i给d吗?其实不是,这里会先开一个double的临时变量,并且临时变量具有常性,先将i给这个double类型的临时变量,然后再将这个临时变量给d。
而自定义类型也一样,其实是先将1给A类型的临时变量,这个临时变量会调用构造函数用1初始化,然后用拷贝构造函数将临时变量给aa2。这样说来我们应该是调用了构造+拷贝构造我们验证一下:
如何确定确实有一个临时变量呢?
我们通过传引用发现会报错初始值必须为不可修改的左值,然后我们加个const试一下:
我们之前说过临时变量具有常性必须加const,在这里我们普通变量会报错而加了const就能正常这就说明了在类型转换期间一定是有临时变量的。
class A
{
public:A(int a):_a1(a){cout << "A(int a)" << endl;}A(const A& d){cout << "A(const A& d)" << endl;_a2 = d._a2;_a1 = d._a1;}private:int _a2;int _a1;
};
int main() {//A aa(1);A aa2 = 1;}
这里为什么只调用了一个构造函数呢?其实这里就是编译器的优化了, 编译器直接优化为用1去构造aa2。也就是说实际过程变成了:构造+拷贝构造+编译器优化-->构造。那如果我们不想让编译器优化有什么办法呢?这里c++引入了explicit关键字,将关键字加到构造函数前面编译器就不能进行类型转换了。如下图:
上面是单参数的构造函数,那如果是多参数的构造函数呢?
如上图所示, 多参数的构造函数也支持只不过需要加大括号,在这里要说明一下,单参数的隐式类型转换是c++98就支持的,而多参数的隐式类型转换是c++11支持的。同样我们在前面加一个explicit就不支持了。
同样编译器还会对函数传参进行优化:
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void func1(A aa)
{}int main()
{A aa1;func1(aa1); //func1(2); //构造 + 拷贝构造 + 优化 -> 构造func1(A(3)); //构造 + 拷贝构造 + 优化 ->构造return 0;
}
通过调试我们发现,func1(2)本来应该是构造+拷贝构造,结果被优化成了构造。func(A(3))也一样,A(3)是一个匿名对象,然后将A(3)拷贝给aa,然后优化为构造。
那么如果函数参数变成传引用还会优化吗?
首先Func2的参数前面必须const,否则连编译都不会通过,因为Func2(2)中2是一个常量。其次我们可以看到当传引用的时候编译器是不会优化的,因为传引用本身就是将自身传过去,没有什么再减少空间开辟的优化了。再看下一个:
这里func3的顺序大家应该都清楚,进入函数后aa先构造,然后值返回会调用拷贝构造,函数要结束时aa调用析构,那么A aa1 = func3()的过程是什么样呢?这个过程按照我们所学的知识来理解应该是构造,拷贝构造,然后再拷贝构造赋值给aa1所以应该是1个构造2个拷贝构造。那么我们验证一下:
我们发现只有1个构造1个拷贝构造,其实这里是被编译器优化了。
当我们直接返回匿名对象的时候发现本来应该构造+拷贝构造直接变成了构造。再看下图:
这个我们发现更神奇了,本来应该是构造+拷贝构造+拷贝构造直接被优化为1个构造,编译器直接用匿名对象初始化aa1了。所以我们得出一个结论:能用匿名对象直接返回的就用匿名对象这样会加速编译器的优化。
编译器优化总结:
1.对象返回
接收返回值对象,尽量拷贝构造方式接收,不要赋值接收。
函数中返回对象时,尽量返回匿名对象。
2.函数传参总结
尽量使用const & 传参
二、static成员
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量,用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
我们先用以前的知识实现一个类,这个类可以计算出创建了多少个对象。
int count = 0;
class Test
{
public:Test(){count++;}
};
int main()
{Test t1;cout << count << endl;return 0;
}
按照我们以前的写法我们发现是无法正常编译的,我们看一下报错信息:
这是因为我们定义的全局变量的名称与库中的某个函数名冲突了,如果是以前我们直接修改变量名称即可,但我们既然学了c++就用c++的方式解决,我们知道c++的类中定义的变量名称是不会与库中的名称冲突的,那我们如何既能将变量写到类中又能让这个变量不属于这个单独的类属于所有的类对象呢?这里就用到了static修饰的变量。如下图:
class Test
{
public:Test(){count++;}static int count;
};
int Test::count = 0;
int main()
{Test t1;cout << t1.count << endl;return 0;
}
static修饰的变量或函数不属于某个对象,属于所有对象,属于整个类。静态成员变量必须在类外进行初始化,类中仅仅是声明。
我们再多加几个变量
这样还不算成功的计算出所有创建对象的数量,看下图:
当我们调用拷贝构造创建一个新的对象的时候发现数量不对了,我们应该要加上拷贝出来的对象的数量,所以我们改进一下:
这样即使我们利用拷贝构造创建新对象也会成功加上数量。
那如果定义的count是私有的该怎么办呢?
私有的很明显是不能直接访问的,所以我们利用函数去返回。
但是如果我们没有创建对象呢?我们不能因为调用一个函数再去创建一个类对象吧,这时候就该静态函数出场了。
class Test
{
public:Test(){count++;}Test(const Test& d){count++;}static int GetCount(){return count;}
private:static int count;
};
int Test::count = 0;
int main()
{cout << Test::GetCount() << endl;return 0;
}
静态函数可以直接用类名访问,并且静态函数内不可以访问普通成员变量。如下图:
静态成员特性:
1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。
2.静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。
3.类静态成员即可用类名::静态成员或者对象.静态成员来访问。
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员.
5.静态成员也是类的成员,受public,protected, private访问限定符的限制。
匿名对象到下一行直接就调用析构函数了所以声明周期只在这一行。
那么匿名对象有什么作用呢?
比如我们需要一个值用来初始化一个对象,然后利用函数返回一个初始化的匿名对象即可。
三丶友元
友元其实我们之前已经接触过了,在运算符重载的时候我们需要重载<<符号用到了友元,友元提供了一种突破封装的方式,有时提供了便利。但是友元增加了耦合度,会破坏封装,所以友元不宜多用。
友元分为友元函数和友元类。
这是我们之前的例子:
class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}
int main()
{Date d;cin >> d;cout << d << endl;return 0;
}
说明:
1.友元函数可访问类的私有和保护成员,但不是类的成员函数。
2.友元函数不能用const修饰。
3.友元函数可以在类定义的任何地方声明,不受类访问限定符的限制。
4.一个函数可以是多个类的友元函数。
5.友元函数的调用和普通函数的调用原理相同
友元类:
class Time
{friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
中的私有成员变量
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}
private:int _hour;int _minute;int _second;
};
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};
如上图所示,当我们创建了一个Date类的时候然后Date类中有私有自定义变量的时候,我们需要对这个自定义变量访问,这个时候就可以在time类中将Date类设为友元,Date类中就可以访问time类中的私有成员。但是友元关系是不可逆的,也就是说Time是不可以使用Date的变量的。
注意:
1.友元关系是单向的,不具有交换性。
2.友元关系不能传递,如果C是B的友元,B是A的友元,不能说明C是A的友元。
3.友元关系不能继承
四丶内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员,外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员,但是外部类不是内部类的友元。
特性:
内部类可以定义在外部类的public,protected,private都是可以的。
注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名
sizeof(外部类)=内部类,和内部类没有任何关系
通过上图我们可以发现,A类的大小是4字节也就是int h的大小,如果包含类B的大小应该是8字节才对,所以sizeof(外部类)与内部类无关。
受类域限制是什么意思呢?看下图:
B对象是无法直接定义的,需要加上域作用限定符。
当然这是类B公有的情况,如果类B是私有呢?
我们发现当类B是私有的时候是无法直接用类B创建对象的,这就证明内部类受外部类的类域限制。
class A
{
private:int h;int k;
public:class B{public:void Print(A& a){cout << a.h << endl;a.k = 10;}private:int b;};
};
int main()
{A aa;A::B bb;return 0;
}
从上图可以发现内部类是外部类的友元类,我们在内部类B中直接访问了A类中的h和k变量。
总结
相关文章:

c++终极螺旋丸:₍˄·͈༝·͈˄*₎◞ ̑̑“类与对象的结束“是结束也是开始
文章目录 前言一.构造函数中的初始化列表 拷贝对象时的一些编译器优化二.static成员三.友元四.内部类总结前言 前两期我们将类和对象的重点讲的差不多了,这一篇文章主要进行收尾工作将类和对象其他的知识点拉出来梳理一遍,并且补充前两篇没有讲过的…...

【Python--torch.nn.functional】F.normalize用法 + 代码说明
【Python–torch.nn.functional】F.normalize介绍 代码说明 文章目录【Python--torch.nn.functional】F.normalize介绍 代码说明1. 介绍2. 代码说明2.1 一维Tensor2.2 二维Tensor2.3 三维Tensor3. 总结1. 介绍 import torch.nn.functional as F F.normalize(input: Tensor, …...

【算法题】1887. 使数组元素相等的减少操作次数
插: 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 坚持不懈,越努力越幸运,大家一起学习鸭~~~ 题目: 给你一个整数数组 nums ࿰…...

GD库图片裁剪指定形状解决办法(PHP GD库 海报)
需求描述:需要把图片裁剪成一个指定的平行四边形,目的是使用GD库,把裁剪后的图片放在底图上面,使最终合成的图片看起来是一个底图平行四边形的样子提示:可以结合本作者的其他文章,来生成一个定制化的海报&a…...

redis的简介及应用场景
1、基本信息 Redis英文官网介绍: Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queri…...

2、HAL库利用滴答定时器systick(1ms中断)实现时间计数戳
文档说明:通过滴答定时器的1ms中断实现时间计数,标记需要的时间标志,在主函数中查询标志,避免延时函数消耗CPU 1、HAL库systick定时器说明 在CubeMx生成的代码main()函数首先执行的函数为HAL_Init();里面会进行滴答定时器初始化…...

Spring入门学习
Spring入门学习 文章目录Spring入门学习Spring概述Spring FrameworkIOCIOC容器DIIOC容器的实现类①FileSystemXmlApplicationContext②ClassPathXmlApplicationContext基于XML管理bean入门案例创建类创建xml在Spring配置文件中配置bean测试Spring概述 Spring 是最受欢迎的企业级…...

webpack(4版本)使用
webpack简介:webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)…...

Linux安装ElasticSearch
下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch 1 版本选择 ElasticSearch 7 及以上版本都是自带的 jdk,假如需要配置指定的 jdk 版本的话,可以在 es 的 bin 目录下找到elasticsearch-env.bat 这个文件&#x…...

Linux中C语言编程经验总结
修改记录 版本号日期更改理由V1.02022-03-15MD化 总则 仅总结一些常用且实用的编程规范和技巧,且避免记忆负担,聚焦影响比较大的20% ! 编译器 打开全warning编译器开关 正例 gcc -W -Wall -g -o someProc main.c反例 gcc -g -o someProc main…...

jvisualvm工具使用
jdk自带的工具jvisualvm,可以分析java内存使用情况,jvm相关的信息。 1、设置jvm启动参数 设置jvm参数**-Xms20m -Xmx20m -XX:PrintGCDetails** 最小和最大堆内存,打印gc详情 2、测试代码 TestScheduleClassGc package com.core.schedule;…...

redis五大IO网络模型、内存回收
目录1.0用户空间和内核态空间1.1 网络模型-阻塞IO1.2 网络模型-非阻塞IO1.3 网络模型-IO多路复用1.3.1 网络模型-IO多路复用-select方式1.3.2 网络模型-IO多路复用模型-poll模式1.3.3 网络模型-IO多路复用模型-epoll函数1.3.4 网络模型-epoll中的ET和LT1.3.5 网络模型-基于epol…...

【C/C++】内存管理详解
目录内存布局思维导图1.C/C内存分布数据段:栈:代码段:堆:2.C语言中动态内存管理方式3.C内存管理方式3.1new/delete操作内置类型3.2new和delete操作自定义类型4.operator new 与 operator delete函数5.new和delete的实现原理5.1内置类型5.2自定…...

Android ProcessLifecycleOwner 观察进程生命周期
文章目录简介使用依赖用法1,结合 LiveData用法2,获取 owner的 lifecycle 实例,并对 lifecycle 添加观察者简介 ProcessLifecycleOwner 直译,就是,进程生命周期所有者。 通过 DOC 注释了解到: Lifecycle.E…...

如何编写一个 npm 插件?
提到写 npm 插件,很多没搞过的可能第一感觉觉得很难,无从下手,其实不然。 我们甚至写个简单的 console.log(hello word),都是可以当成一个插件发布上去的。 其实无从下手的主要难点还是在于你的具体要做的功能逻辑,这…...

mapstruct- 让VO,DTO,ENTITY转换更加便捷
mapstruct- 让VO,DTO,ENTITY转换更加便捷 1. 简介 MapStruct是一个代码生成器,简化了不同的Java Bean之间映射的处理,所谓映射指的就是从一个实体变化成一个实体。例如我们在实际开发中,DAO层的实体和一些数据传输对…...

IAR警告抑制及还原
工作中需要临时抑制 警告 Pa084,源代码如下: sy_errno_t sy_memset_s(void *dest, sy_rsize_t dmax, int value, sy_rsize_t n) { sy_errno_t err; if (dest NULL) { return SY_ESNULLP; } if (dmax > SY_RSIZE…...

工厂模式(Factory Pattern)
1.什么是工厂模式 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 2.工厂模式的作用 实现创建者和调用者的分离 3.工厂模式的分类 简单工厂模式工厂方法模式抽象工厂模式 4.工厂模式的优缺点 优…...

JavaScript语法学习--《JavaScript编程全解》
《JavaScript编程全解》 JavaScript琐碎基础 0.前言 1.RN: react native是Facebook于2015年4月开源的跨平台移动应用开发框架,是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物,支持iOS和安卓两大平台。 2.ts与js js:是弱…...

linux安装极狐gitlab
1. 官网寻找安装方式 不管我们使用任何软件,最靠谱的方式就是查看官方文档。gitlab提供了相应的安装文档,并且有对应的中文文档。地址如下: https://gitlab.cn/install/ 我在这里以CentOS作为安装示例,大家可根据自己的需要选择…...

软考高级信息系统项目管理(高项)原创论文——人力资源管理
人力资源管理 某市某国有装备制造公司智能安防信息管控平台项目是在公司推进企业信息化进程和实现企业可持续发展的背景下于2016年8月提出来的,我公司积极应标并最终顺利中标,而我有幸被任命为项目经理,担任起该项目的管理工作。该项目投资金额为530万元,其中软件部分为360…...

Java Lambda表达式 匿名内部类 函数式接口(FunctionalInterface)
Java Lambda表达式定义背景示例匿名类实现Lambda表达式实现对比匿名类和Lambda实现Lambda表达式(调用)说明Lambda表达式的语法Java 1.8 新特性:函数式接口jdk 1.8 自带的函数式接口 (举例)定义 参考Oracle官网&#x…...

javaEE 初阶 — 流量控制与拥塞控制
文章目录1. 流量控制2. 拥塞控制TCP 工作机制:确认应答机制 超时重传机制 连接管理机制 滑动窗口 1. 流量控制 流量控制是一种干扰发送的窗口大小的机制,滑动窗口,窗口越大,传输的效率就越高(一份时间,…...

HTML自主学习 - 2
一、表格 基本语法 <table><tr><td>单元格内容1</td><td>单元格内容2</td><td>单元格内容3</td></tr></table> 1、<table> </table>标签用于定义表格 2、<tr> </tr>标签用于定义表格的…...

【转载】通过HAL库实现MODBUS从机程序编写与调试-----STM32CubeMX操作篇
通过HAL库实现MODBUS从机程序编写与调试-----STM32CubeMX操作篇[【STM32】RS485 Modbus协议 采集传感器数据](https://blog.csdn.net/qq_33033059/article/details/106935583)基于STM32的ModbusRtu通信--ModbusRtu协议(一)基于STM32的ModbusRtu通信--终极Demo设计(二)STM32RS48…...

【C++】string类(上)
文章目录1.为什么要学习string类2.标准库中的string类1.string分类2.string类对象的常见构造1.string3. string类对象的容量操作1.size2.capacity3.reserve4.resize扩容初始化删除数据4. string类对象的修改操作1.push_back2.append3.operator1.为什么要学习string类 c语言的字…...

Java泛型
文章目录一、泛型介绍1. 背景2. 概念3. 好处二、泛型声明泛型类型符号泛型声明方式三、类型擦除1. 什么是类型擦除桥接方法2. 为何需要类型擦除3. 类型信息并未完全擦除四、泛型使用1. 泛型类2. 泛型接口3. 泛型方法五、泛型扩展1. 泛型的上下边界泛型的上边界泛型的下边界2. 泛…...

07 分布式事务Seata使用(2)
1、Seata是什么 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS&#x…...

c++练习题5
5.在C语言中,程序运行期间,其值不能被改变的量叫 常量 。 6.符号常量是指用一个符号名代表一个常量。 7.整型常量和浮点型常量也称为 数值常量 ,它们有正负之分。 9.在C中,变量是 其值可以改变的量 。 …...

Python 高级编程之正则表达式(八)
文章目录一、概述二、正则表达式语法1)字符匹配2)字符集合3)定位符4)分组1、定义分组2、引用分组3、命名分组三、Python 的 re 模块1)re.match() 方法2)re.search() 方法3)re.match() 与 re.sea…...