类和对象:完结

1.再深构造函数

之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅
式,就是初始化列表,初始化列表的使⽤⽅式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成
员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式;
每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义 初始化的地⽅
引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始 化,否则会编译报错;(初始化列表就是他们俩初始化的地方)
#include<iostream>
using namespace std;
class A
{
public://如果不想让类型转换发生,可以在构造函数前加explicit函数/*explicit A(int a = 0){_a1 = a;}*///多参数也是如此A(int a = 0)//单参数可以{_a1 = a;}A(int a1, int a2){_a1 = a1;_a2 = a2;}A(const A& aa){_a1 = aa._a1;}void Print(){cout << _a1 << " " << _a2 << endl;}
private:int _a1 = 1;int _a2 = 2;
};class Stack
{
public:void Push(const A& aa)//临时对象具有常性,要加const{//...}
private:A _arr[10];int _top;
};
int main()
{A aa1(1);//调用构造aa1.Print();// 隐式类型转换,类型转换会生成中间变量// 2先构造⼀个A的临时对象,再⽤这个临时对象拷⻉构造aa2// 编译器遇到连续构造+拷⻉构造->优化为直接构造//调试观察A aa2 = 2;aa2.Print();A& raa1 = aa2;//A& aa2 = 2;可以验证://并不是直接构造,会生成临时变量,具有常性const A& aa2 = 2;//真正用意:Stack st;A aa3(3);st.Push(aa3);//写起来更简单,消耗是一样的st.Push(3);//多参数://A aa4 = 1, 1;//不支持//但是:C++11后才支持多参数转换A aa4 = { 1, 1 };const A& raa5 = { 2,2 };st.Push(aa4);st.Push({ 2,2 });return 0;
}

注意:初始化列表是他定义的地方,那么每个成员都会去走初始化列表,哪怕是不写

还有一类:自定义类型:

EG:

class Time
{
public:Time(int hour = 1):_hour(hour){cout << "Time()" << endl;}
private:int _hour;
};

不在初始化列表中定义, 不写也走:只调用Time的默认构造函数(无参/全缺省)

如果Time没有默认构造,调不到,只能自己传,爱传什么传什么:

无默认构造:

class Time
{
public:Time(int hour):_hour(hour){cout << "Time()" << endl;}
private:int _hour;
};
class Date
{
public:Date(int& xx, int year, int month, int day): _year(year), _month(month), _day(day), _n(1), _ref(xx), _t(1){}void Print()const{cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private://声明int _year;int _month;int _day;const int _n;int& _ref;Time _t;
};

初始化列表和函数体内可以混着赋值:
EG:(初始化列表和函数体可以打配合)

Date():_ptr((int*)malloc(12))
{//初始化列表和函数体内可以混着赋值if (_ptr == nullptr){perror("malloc fail");}else{memset(_ptr, 0, 12);}
}
C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的 成员使⽤的;
(注:定义是有开空间,下列是声明)C++11及后
private://声明,后面为缺省值->初始化列表用的int _year = 1;int _month = 1;int _day = 1;;
可以通过缺省函数理解,只不过是给初始化类表用的;
(结合下图理解):
尽量使⽤初始化列表初始化,因为那些你不在初始化列表初始化的成员也会⾛初始化列表,如果这 个成员在声明位置给了缺省值,初始化列表会⽤这个缺省值初始化。如果你没有给缺省值,对于没 有显⽰在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有 显⽰在初始化列表初始化的⾃定义类型成员会调⽤这个成员类型的默认构造函数,如果没有默认构 造会编译错误;
注意与默认构造函数区分:
可以无参调用的才是默认构造:(以下并不是)
#include<iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month)//, _day(day)对于内置类型,是不做处理的,可能是个随机值{}void Print()const{cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private://声明,后面为缺省值->初始化列表用的int _year = 1;int _month = 1;int _day = 1;;
};int main()
{Date d1(2024, 7, 14);d1.Print();Date d2;//会报错,所以并不是默认构造return 0;
}
因此:让编译器自动生成默认构造便可行:
还有:不仅仅可以给以上这种缺省值:
private:int _year = 1;int _month = 1;int _day = 1;;int* _ptr = (int*)malloc(12);Time _t = 1;
初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆ 关。建议声明顺序和初始化列表顺序保持⼀致;
例题:
下⾯程序的运⾏结果是什么(D)
A. 输出 1 1
B. 输出 2 2
C. 编译报错
D. 输出 1 随机值
E. 输出 1 2
F. 输出 2 1
#include<iostream>
using namespace std;
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2 = 2;int _a1 = 2;
};
int main()
{A aa(1);aa.Print();
}
因为定义是先_a2后_a1,先对_a2进行初始化,这时候_a1的缺省值还没发现,给_a2的初始化就是编译器自己决定的,为_a1的随机值,将_a1的值给了_a2,再到_a1,将1的值给了_a1,所以answer是D!(调试很快发现)
总结:尽可能写初始化列表
每个成员都要走初始化列表,每个构造函数都有初始化列表
1.在初始化列表初始化的成员:直接用;(显示写)
2.没有在初始化列表的成员(不显示写)
----a.声明的地方有缺省值用缺省值
----b.没有缺省值
--------x.内置类型:不确定,看编译器,大概率是随机值
--------y.自定义类型:调用默认构造,没有默认构造就编译报错
3.引用,const没有默认构造的自定义,必须在初始换列表初始化
4.不要把初始化列表和默认构造扯上关系
5. 初始化列表中按照成员变量在类中声明顺序(在内存中存放的顺序)进⾏初始化
6.声明中的缺省值是给初始化列表用的,缺省值是不走初始化列表的

2.类型转换

C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数
构造函数前⾯加explicit就不再⽀持隐式类型转换

3.static成员

⽤static修饰的成员变量,称之为静态成员变量, 静态成员变量⼀定要在类外进⾏初始化 ;
#include<iostream>
using namespace std;
class A
{
public:A(){++_scount;}A(const A& t){++_scount;}~A(){--_scount;}
private:// 类⾥⾯声明static int _scount;
};
// 类外⾯初始化
int A::_scount = 0;
int main()
{return 0;
}
静态成员变量为当前类的所有对象所共享, 不属于某个具体的对象 ,不存在对象中,存放在静态区;
⽤static修饰的成员函数,称之为静态成员函数, 静态成员函数没有this指针 ;
如果:_scount是私有的,那么指定类域的方式就行不通了,这时候就可以提供一个静态的成员函数:
static int GetACount()
{return _scount;
}
静态成员函数中可以访问其他的静态成员,但是 不能访问⾮静态 的,因为没有this指针;
static int GetACount()
{_a++;//报错//不能访问⾮静态的,因为没有this指针return _scount;
}
⾮静态的成员函数,可以访问任意的静态成员变量和静态成员函数;
void func()
{cout << _scount << endl;cout << GetACount() << endl;
}
突破类域就可以访问静态成员,可以通过"类名::静态成员"或者"对象.静态成员"来访问静态成员变量和静态成员函数;
cout << A::GetACount() << endl;
cout << a1.GetACount() << endl;
静态成员也是类的成员,受public、protected、private 访问限定符的限制;(类里面定义的静态成员变量某种程度上可以理解为静态的全局,只是受类域的限制,也就是他的生命周期是全局的,且不属于某个对象,严格来说只能在所在的类里面使用,类外面也行,只是受访问限定符的限制)
静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不⾛构造函数初始化列表;

4.友元

友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯;
外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数;
友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制;
⼀个函数可以是多个类的友元函数;(我不仅仅和你,还要和别人做朋友)
#include<iostream>
using namespace std;
// 前置声明,不然则A的友元函数声明编译器不认识B//-----注意点
class B;//-----解决点
class A
{// 友元声明friend void func(const A& aa, const B& bb);//-----问题点
private:int _a1 = 1;int _a2 = 2;
};
class B
{// 友元声明friend void func(const A& aa, const B& bb);
private:int _b1 = 3;int _b2 = 4;
};
void func(const A& aa, const B& bb)
{cout << aa._a1 << endl;//~~~~~~~~~~~~~~~~~~~~~~~~~cout << bb._b1 << endl;//一个函数成为多个类的友元
}
int main()
{A aa;B bb;func(aa, bb);return 0;
}

友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员;

#include<iostream>
using namespace std;
class A
{// 友元声明friend class B;//把B定义成A的友元类-----解决点---B的成员函数都成为了A的友元函数
private:int _a1 = 1;int _a2 = 2;
};
class B
{
public://B要一直访问A类的私有保护成员-----问题点void func1(const A& aa){cout << aa._a1 << endl;cout << _b1 << endl;}void func2(const A& aa){cout << aa._a2 << endl;cout << _b2 << endl;}
private:int _b1 = 3;int _b2 = 4;
};
int main()
{A aa;B bb;bb.func1(aa);bb.func1(aa);return 0;
}

友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元;

友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不一定是C的友元;
有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多⽤;

5.内部类 

如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在全局相⽐,他仅仅只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类;(内类不是属于外类的成员)
内部类默认是外部类的友元类;(我是你的朋友,所以我可以访问你的私有)
内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使⽤,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的 专属内部类 ,其
他地⽅都⽤不了;
#include<iostream>
using namespace std;
class A
{
private:static int _k;int _h = 1;
public:class B // B默认就是A的友元{public:void foo(const A& a){cout << _k << endl; //OKcout << a._h << endl; //OK}private:int _b = 1;};
};
int A::_k = 1;
int main()
{cout << sizeof(A) << endl;//4:实际上只有一个_h,没带_b//B不是A的成员A::B b;//指定B是属于A的类域(受访问限定符限制)A aa;b.foo(aa);return 0;
}

6.匿名对象

⽤"类型(实参)"定义出来的对象叫做匿名对象,相⽐之前我们定义的"类型 对象名(实参)"定义出来的叫有名对象;
匿名对象⽣命周期只在当前⼀⾏,⼀般临时定义⼀个对象当前⽤⼀下即可,就可以定义匿名对象;( 他的⽣命周期只有这⼀⾏,我们可以看到下⼀⾏他就会⾃动调⽤析构函数 )
就是为了更方便;
#include<iostream>
using namespace std;class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};
int main()
{A aa1;A aa2(2);// 不能这么定义对象,因为编译器⽆法识别下⾯是⼀个函数声明,还是对象定义//A aa1();// 但是我们可以这么定义匿名对象,匿名对象的特点不⽤取名字,// 但是他的⽣命周期只有这⼀⾏,我们可以看到下⼀⾏他就会⾃动调⽤析构函数A();A(1);// 匿名对象在这样场景下就很好⽤,当然还有⼀些其他使⽤场景,这个我们以后遇到了再说cout << Solution().Sum_Solution(10) << endl;//就是为了更方便//有名Solution st;cout << st.Sum_Solution(10) << endl;return 0;
}

7.对象拷贝时的编译器优化

现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传返回值过程中可以省略的拷⻉;
如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译还会进⾏跨⾏跨表达式的合并优化;
示例类:
#include<iostream>
using namespace std;
class A
{
public:A(int a = 0):_a1(a){cout << "A(int a)" << endl;}A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a1 = aa._a1;}return *this;}~A(){cout << "~A()" << endl;}void Print(){cout << "A::Print " << _a1 << endl;}
private:int _a1 = 1;
};
单参数类型的构造函数的隐式类型转化的优化示例:
int main()
{//单参数类型的构造函数的隐式类型转化A aa1 = 1;//构造加拷贝构造(构造临时对象,对临时对象进行拷贝构造)(因为类型转化会产生临时对象)//开始执行发现:编译器优化成直接拷贝const A& aa2 = 1;//生成临时对象,是引用,没有拷贝构造return 0;
}

传参优化示例:

void f1(A aa)//传值传参---会产生一个拷贝
{}
void f2(A& aa)
{}
int main()
{A aa1(1);//构造//f1(aa1);//实参传给形参进行拷贝:调用构造,拷贝构造//不会优化:一般是在连续一个步骤才有优化//但是不排除其他更激进的cout << endl;//用引用:减少拷贝构造f2(aa1);cout << endl;//用匿名对象:在连续过程f1(A(1));//优化成直接构造cout << endl;f1(1);//本意是1走隐式类型转化,生成临时对象,再拷贝构造:优化成直接构造cout << endl;return 0;
}

 传返回值优化示例:

A f3()//传值传参返回:会产生一个临时对象:因为aa出了作用域就销毁了
{A aa(1);return aa;//产生临时对象
}
//应该是aa作为局部对象,出了作用域销毁,调用~A(),由aa产生临时对象调用Print
//VS2022优化:严格来说没有生成aa,合二为一,只生成临时对象:看执行结果可推
int main()
{f3().Print();//因为临死对象:构造+拷贝构造//用产生的临时对象调用Print//临时对象的周期就在这一行cout << endl;return 0;
}
A f4()
{A aa(1);//构造return aa;//拷贝构造
}
//优化:省略了临时对象,VS2022更激进,连aa都省掉了建议用2019-debug观察
int main()
{A ret = f4();//拷贝构造ret.Print();	cout << endl;return 0;
}
A f4()
{A aa(1);//构造cout << "~~~~~~~~~" << endl;//构造了aa,aa充当临时对象return aa;//拷贝构造
}
int main()
{A ret;ret = f4();//赋值//赋值完后。临时对象析构ret.Print();	cout << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/48893.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通信原理-思科实验三:无线局域网实验

实验三 无线局域网实验 一&#xff1a;无线局域网基础服务集 实验步骤&#xff1a; 进入物理工作区&#xff0c;导航选择 城市家园; 选择设备 AP0&#xff0c;并分别选择Laptop0、Laptop1放在APO范围外区域 修改笔记本的网卡&#xff0c;从以太网卡切换到无线网卡WPC300N 切…

力扣Hot100-543二叉树的直径

给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5] 输出&a…

C++ 基础(类和对象下)

目录 一. 再探构造函数 1.1. 初始化列表&#xff08;尽量使用列表初始化&#xff09; 二. static成员 2.1static成员初始化 三.友元 3.1友元&#xff1a;提供了⼀种 突破类访问限定符封装的方式. 四.内部类 4.1如果⼀个类定义在另⼀个类的内部&#xff0c;这个内部类就叫…

2024.7.24 作业

1.二叉树的创建、遍历自己实现一遍 bitree.h #ifndef BITREE_H #define BITREE_H#include <myhead.h>typedef char datatype;typedef struct Node {datatype data;struct Node *left_child;struct Node *right_child; }Node,*BiTreePtr;//创建二叉树 BiTreePtr tree_cr…

我在百科荣创企业实践——简易函数信号发生器(5)

对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的…

【Java语法基础】9.异常处理

9. 异常处理 Error是程序无法处理的错误&#xff0c;出现时线程被JVM终止。 Exception&#xff0c;指的是程序运行时可以处理的异常。其继承关系如下表&#xff1a; 运行时异常&非运行时异常 运行时异常 都是RuntimeException类及其子类异常&#xff0c;如NullPointerE…

模拟实现c++中的string

c内置string库的相关函数&#xff1a;string - C Reference 目录 一string类构造&#xff0c;拷贝构造和析构&#xff1a; 二string内正向迭代器实现&#xff1a; 三赋值运算符重载实现&#xff1a; 四reserve&#xff0c;empty&#xff0c;clear实现&#xff1a; 五push_b…

动手学深度学习——6.循环神经网络

1.序列模型 处理序列数据需要统计工具和新的深度神经网络架构。 为了简单起见&#xff0c;我们以 图8.1.1所示的股票价格&#xff08;富时100指数&#xff09;为例。 图8.1.1 近30年的富时100指数 其中&#xff0c;用&#x1d465;&#x1d461;表示价格&#xff0c;即在时间…

LIS检验信息软件源码,适合二级医院的应用

LIS系统主要面向医院检验科&#xff0c;包含检验医生日常处理、报告处理、质量控制、条码管理、仪器双工通讯、无人值守等诸多功能模块&#xff0c;能与HIS系统、体检系统和电子病历信息系统实现无缝连接&#xff0c;已成功应用于多家各种规模的医院&#xff0c;满足客户各方面…

Git之repo sync -c与repo sync -dc用法区别(四十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

InternLM学习笔记

入门岛 1. Linux基础知识 2. Python 基础知识 from collections import Countertext """ Got this panda plush toy for my daughters birthday, who loves it and takes it everywhere. Its soft and super cute, and its face has a friendly look. Its a …

论文阅读【检测】:Facebook ECCV2020 | DETR

文章目录 论文地址AbstractMotivation模型框架详细结构小结 论文地址 DETR Abstract 提出了一种将目标检测视为直接集预测问题的新方法。简化了检测pipeline&#xff0c;有效地消除了许多手工设计的组件的需求&#xff0c;例如非最大抑制过程或锚生成&#xff0c;这些组件明…

设计模式|观察者模式

观察者模式是一种行为设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时&#xff0c;它的所有观察者都会收到通知并更新。观察者模式常用于实现事件处理系统、发布-订阅模式等。在项目中&#xff0c…

磁盘管理与磁盘卷--红帽Linux操作系统<>

分区的两种格式 1、MBR分区 MBR(Master Boot Record&#xff0c;主引导记录)是传统的分区机制&#xff0c;使用BI0S引导PC设备&#xff0c;寻址空间只有32bit长。 分区空间最大支持2.2TB 支持的分区数量:4个主分区或者3个主分区1个扩展分区 为什么MBR最多只能有4个主分区?…

云服务部署项目(Spring + Vue)

云计算&#xff1a;腾讯云 操作系统&#xff1a;Ubuntu 22.04.4 LTS 项目&#xff1a;若依前后端分离项目&#xff08;SpringBoot Vue&#xff09; 首先要安装基本的一些依赖环境&#xff0c;大家可以看一下我往期的文章&#xff1a; Ubuntu在线JDK Ubuntu在线安装Nginx Ubunt…

文件解析的终极工具:Apache Tika

文件解析的终极工具&#xff1a;Apache Tika Apache Tika 简介 Apache Tika 是一个开源的、跨平台的库&#xff0c;用于检测、提取和解析各种类型文件的元数据。 它支持多种文件格式&#xff0c;包括文档、图片、音频和视频。 Tika是一个底层库&#xff0c;经常用于搜索引擎…

Android 列表或网格形式展示大量数据:RecyclerView

目录 RecyclerView是什么如何使用RecyclerView 涉及到的类LayoutManager为Item设置不同的布局样式制作拖动的RecyclerView 一、RecyclerView是什么 RecyclerView是Android支持库中的一个控件&#xff0c;用于在列表或网格形式展示大量数据。它是ListView的升级版&#xff0c…

《梦醒蝶飞:释放Excel函数与公式的力量》18.1 图表类型与设计

第18章&#xff1a;创建图表和数据可视化 18.1 图表类型与设计 Excel提供了多种图表类型&#xff0c;帮助用户以直观的方式展示数据。选择合适的图表类型和设计可以显著提高数据的可读性和理解度。以下将介绍常见的图表类型及其应用&#xff0c;并通过具体案例进行说明。 18.…

如何利用Jenkins自动化管理、部署数百个应用

目录 1. Jenkins 安装与部署步骤 1.1 系统要求 1.2 安装步骤 1.2.1 Windows 系统 1.2.2 CentOS 系统 1.3 初次配置 2. Gradle 详细配置方式 2.1 安装 Gradle 2.1.1 Windows 系统 2.1.2 CentOS 系统 2.2 配置 Jenkins 中的 Gradle 3. JDK 详细配置方式 3.1 安装 JD…

Java:防止输入输出超时

一、防止输入超时 当我们直接使用Scanner进行输入操作的时候&#xff0c;每次读取输入的数据都会进行一次硬盘的IO操作&#xff0c;这个操作是很慢的&#xff0c;如果要读取的数据过多&#xff0c;那么我们在刷题网站上就很有可能因为多次的数据读取操作产生超时&#xff01;那…