剖析“类和对象” (下) -------- CPP

news2024/11/27 22:22:46

学习完“类和对象”(上)【剖析“类和对象” (上) -------- CPP】和(中)【剖析“类和对象” (中) -------- CPP】,相信各位同学对CPP中类与对象的理解或多或少都加深了一点。

本篇博客将和大家一同再次学习CPP中类和对象的知识点,跟随本篇博客的脚步定能为你的装备库再增添上不少弹药。 

本篇文章的学习继续借用Date类来辅助对知识点的理解。 

class Date
{
public:
    Date(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }

    Date& operator=(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }

private:
    int _year;
    int _month;
    int _day;
};

一、初始化列表

在上一篇博客中我们学习了类中的默认成员函数,在创建类对象时,编译器通过调用类的构造函数,在构造函数的函数体内给对象中各个成员变量设置一个合适的初始值。

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量 的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始 化一次而构造函数体内可以多次赋值

为了对一个类对象中的成员变量进行初始化,引出了一个新的CPP语法规则 —— 初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量”后面跟一个放在括号中的初始值或表达式

class Date
{
public:
    Date(int year, int month, int day)
        : _year(year)
        , _month(month)
        , _day(day)
    {}

private:
    int _year;
    int _month;
    int _day;
};

※ 在初始化列表中,成员变量只能初始化一次。

学习了什么是初始化列表后,那为什么要有初始化列表呢,有什么用呢?

在类中会存在以下场景,初始化列表在以下场景就必不可少了。

① const成员

由于const修饰的特性,在定义的时候必须初始化。const成员只有一次初始化的机会,这次初始化的机会在定义const成员的时候。

② 引用类型的成员变量

引用与const的原因类似,都是只有一次初始化都是机会,就是在定义它的时候。

③ 自定义类型成员(且该类中没有默认构造函数)

【注意】

  • 尽量使用初始化列表初始化。实例化类对象时,编译器会调用类的构造函数,每个成员变量都会走初始化列表,就算不显示在初始化列表中写也会走。
  • 成员变量在类中声明的次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的排列先后顺序无关。
    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();
    }

  • 如果在初始化列表显示写了就用显示写的语句来对成员进行初始化。
  • 如果没有在初始化列表显示初始化。1. 对于内置类型,若在类中成员变量声明处给了缺省值就用缺省值,若没有缺省值就用随机值。2. 对于自定义类型,调用它对应的默认构造函数,如果该类没有默认构造函数编译器就报错。

类的隐式类型转换与explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无缺省值其余均有缺省值的构造函数,还具有类型转换的作用。

1. 单参数构造函数支持的隐式类型转换

class Date
{
public:
    Date(int year)
        : _year(year)
    {}

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(2023);
    Date d2 = 2023;  // ① 隐式类型转换
    const Date& d3 = 2023;  // ② 隐式类型转换
    
    return 0;
}

此处转换的本质是:

用整型2023构造一个Date类型的临时对象,再用临时对象拷贝构造d2;

 用整型2023构造一个Date类型的临时对象,d3则是该临时对象的一个引用。

2. 多参数构造函数支持的隐式类型转换

1)多参数构造,有缺省值

class Date
{
public:
    Date(int year, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day)
    {}

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1 = 2023;
    
    return 0;
}

该类型的构造函数的参数month和day有缺省值,实际上只需要传一个参数给year即可。(注:此处参数使用全缺省也是可以的)

2)多参数构造,无缺省值 —— C++11语法支持

class Date
{
public:
    Date(int year, int month, int day)
        : _year(year)
        , _month(month)
        , _day(day)
    {}

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1 = { 2023, 1, 24 };  // 此处传参需要注意格式,使用{}进行传参
    const Date& d2 = { 2023, 1, 24 };  // 此处传参格式同上

    return 0;
}

上述代码的可读性不是特别好,若不想代码中的类发生隐式类型转换可以在构造函数前用关键字explicit修饰

class Date
{
public:
    explicit Date(int year)
        : _year(year)
    {}

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1 = 2023;  // error

    return 0;
}

二、static修饰的静态成员(变量&函数)

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数(静态成员变量一定要在类外进行初始化)

| static静态成员的特性 |

  1. 静态成员所有类对象所共享,不属于某个具体的对象,存放在静态区。
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 的格式来访问。
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制。

| static成员变量 |

class A
{
public:
	A(int a = 0)
		:_a(a)
	{}

private:
	int _a;
	static int _N;
};
int A::_N = 0;

| static成员函数 |

class A
{
public:
	A(int a = 0)
		:_a(a)
	{}

	static int getN()
	{
		return _N;
	}

private:
	int _a;
	static int _N;
};
int A::_N = 0;

int main()
{
	cout << A::getN() << endl;  // static修饰的成员函数可以无需实例化对象就能调用类中的函数

	return 0;
}

※ 由于static成员函数没有this指针,所以在static成员函数内部无法访问其他的非static成员变量

三、友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以 友元不宜多用。

友元分为:友元函数友元类

1. 友元函数 

问题:现在尝试在Date类中重载operator<<,然后发现没办法将operator<<重载成成员函数。

class Date
{
public:
    Date(int year, int month, int day)
        : _year(year)
        , _month(month)
        , _day(day)
    {}
    
    // d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
    // 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
    ostream & operator<<(ostream& _cout)
    {
        _cout << _year << "-" << _month << "-" << _day << endl;
        return _cout;
    }

private:
    int _year;
    int _month;
    int _day;
};

因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。

所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。(operator>>同理)

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;
}

| 友元函数的特性 |

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

2. 友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员

class Time
{
    friend class Date;  // 声明Date类为Time类的友元类,则在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)
    {
        // 直接访问Time类私有的成员变量
        _t._hour = hour;
        _t._minute = minute;
        _t._second = second;
    }

private:
    int _year;
    int _month;
    int _day;
    Time _t;
};
  • 友元关系是单向的,不具有交换性。(比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行)
  • 友元关系不能传递。(如果C是B的友元, B是A的友元,则不能说明C时A的友元)
  • 友元关系不能继承。

四、匿名对象

匿名对象顾名思义就是没有名字的对象,实例化该对象时不用绞尽脑汁给它取名字,在某些特定的场景下就特别适用。

class A
{
public:
	A(int a = 0)
		:_a(a)
	{}

private:
	int _a;
};

int main()
{
	A();  // A类的一个匿名对象
	A(10);  // A类的一个匿名对象

    A aa();  // error 不能这么定义对象,因为编译器无法识别这是一个函数声明,还是对象定义

	return 0;
}

匿名对象的生命周期只有在其当前所在的那一行代码,当执行完那一行代码,则自动调用其析构函数。

五、编译器对拷贝对象的一些优化

在传参和传返回值的过程中,一般编译器会做一些优化,通过减少调用拷贝构造函数来增加效率,这个在一些场景下还是非常有用的。


本次与大家一起学习CPP中关于“类和对象”(下)的内容到这就已经接近尾声了,期待下次与你相遇。

b8684054acbc4c178cabc301c68046bc.gif

< 你的关注点赞评论收藏都是对我创作最大的鼓励 > 

( 若本篇博客存在错误,望指出,感谢! )

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

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

相关文章

Java两大工具库:Commons和Guava(1)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客。值此新春佳节&#xff0c;我给您拜年啦&#xff5e;祝您在新的一年中所求皆所愿&#xff0c;所行皆坦途&#xff0c;展宏“兔”&#xff0c;有钱“兔”&#xff0c;多喜乐&#xff0c;常安宁&#xff01;Java的成功很大…

Kettle(8):删除组件

删除组件能够按照指定条件,将表中的数据删除。 1 需求 有以下一个文本文件,文本文件包含了要删除的两个用户id: id 392456197008193000 267456198006210000 需要使用Kettle将文本文件中两个ID对应的t_user1表的数据删除。 2 构建Kettle数据流图 效果图: 1 将文本文件输…

【算法突击】动态规划系列 (一)| 程序员面试 | 最大子数组和 | 最长递增子序列 | 最长公共子序列

【算法突击】动态规划系列 &#xff08;一&#xff09;| 程序员面试 | 最大子数组和 | 最长递增子序列 | 最长公共子序列 文章目录【算法突击】动态规划系列 &#xff08;一&#xff09;| 程序员面试 | 最大子数组和 | 最长递增子序列 | 最长公共子序列1. 最大子数组和1.1 题目…

C 语言零基础入门教程(十三)

函数指针 函数指针是指向函数的指针变量。 通常我们说的指针变量是指向一个整型、字符型或数组等变量&#xff0c;而函数指针是指向函数。 函数指针可以像一般函数一样&#xff0c;用于调用函数、传递参数。 函数指针变量的声明&#xff1a; typedef int (*fun_ptr)(int,i…

ATAC-seq分析:Annotating Peaks(9)

1. 注释开放区域 将已识别的无核小体区域与基因组特征&#xff08;如基因和增强子&#xff09;相关联通常很有趣。 一旦注释到基因或增强子的基因&#xff0c;我们就可以开始将 ATACseq 数据与这些基因的特征相关联。 &#xff08;功能注释、表达变化、其他表观遗传状态&#x…

瑞吉外卖实战

https://blog.csdn.net/weixin_43715214/category_12022798.html大佬记录项目介绍day01功能架构&#xff08;1&#xff09;用户层本项目中在构建系统管理后台的前端页面&#xff0c;我们会用到H5、Vue.js、ElementUI等技术。而在构建移动端应用时&#xff0c;我们会使用到微信小…

计算机网络连环炮40问

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

Day09 C++STL入门基础知识六——deque容器 构造函数-赋值操作-大小操作-插入删除-数据存储-排序操作【全面深度剖析+例题代码展示】

无人问津的日子&#xff0c;我为自己喝彩&#xff01; 文章目录1. 基本概念1.1 功能1.2 与vector区别1.3 图解1.4 内部工作原理1.5 deque 容器的迭代器也是支持随机访问的2. 构造函数2.1 函数原型2.2 代码展示2.3 测试结果2.4 小think2.4.1 小问题2.4.2 思路2.4.3 修改2.4.4 测…

从零开始的数模(六)python在高等数学和线性代数中的应用

科学计算设计数值计算和符号计算&#xff0c;在python中作基础数值计算用numpy和scipy工具库&#xff0c;作符号运算用sympy工具库 sympy工具库–符号运算 符号运算基本知识 1.利用symbols函数创建符号变量构造多个符号变量时中间以空格分隔 2.利用符号变量创建表达式 3.利用…

Hive整合HBase,操作HBase表

Hive over HBase原理 Hive与HBase利用两者本身对外的API来实现整合&#xff0c;主要是靠HBaseStorageHandler进行通信&#xff0c;利用 HBaseStorageHandler&#xff0c;Hive可以获取到Hive表对应的HBase表名&#xff0c;列簇以及列&#xff0c;InputFormat和 OutputFormat类&…

STS:Surround-view Temporal Stereo for Multi-view 3D Detection——论文笔记

参考代码&#xff1a;None 1. 概述 介绍&#xff1a;这篇文章提出的方法是对LSS中深度估计部分进行改进&#xff0c;其改进的点是在深度估计部分引入立体匹配去估计周视相机下的深度信息&#xff0c;其中立体匹配使用前后视频帧进行构建&#xff08;可以看作是时序信息的使用&…

node-sass安装失败的解决方案

Nodejs 新版安装过程需要安装node-sass模块&#xff0c;开始一直无法安装成功&#xff0c;网上找了很多方法都无法解决&#xff0c;找了很久才找到的解决方案。 1.1 node-sass安装前准备 Option 2: Install dependencies and configuration manually Install Visual C Build E…

Python处理zip压缩文件

文章目录ZipFile对象写入压缩文件读取和解压缩常用属性ZipInfoZipFile对象 顾名思义&#xff0c;zipfile是处理zip文件的模块&#xff0c;其中最重要的类是ZipFile&#xff0c;其构造函数为 ZipFile(file, moder, compressionZIP_STORED, allowZip64True, compresslevelNone,…

使用VGG网络训练发生错误RuntimeError: CUDA out of memory解决方案:

问题在使用VGG网络训练Mnisist数据集时&#xff0c;发生错误RuntimeError: CUDA out of memory. Tried to allocate 392.00 MiB (GPU 0; 2.00 GiB total capacity; 1.45 GiB already allocated; 0 bytes free; 1.47 GiB reserved in total by PyTorch) If reserved memory is &…

发布详解 | Flutter 3.7 稳定版发布

新年伊始&#xff0c;由 Flutter 3.7 正式版来「打头阵」&#xff01;我们与整个 Flutter 社区成员们继续在 Flutter 3.7 中优化了框架&#xff0c;包括创建自定义菜单栏和层叠式菜单、更好的国际化工具支持、新的调试工具以及其他功能和特性等。新的稳定版里&#xff0c;我们在…

JUC面试(十二)——AQS

AQS juc.locks包下 AbstractQueuedSynchronizer&#xff0c;抽象的队列同步器 aqs是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石&#xff0c; 通过内置的FIFO队列来完成资源获取线程的排队工作&#xff0c;并通过一个int类变量表示持有锁的状态&#x…

极限运算法则——“高等数学”

各位CSDN的uu们你们好啊&#xff0c;今天&#xff0c;小雅兰学习的内容是极限运算法则 回顾 无穷小的极限运算法则 定理1&#xff1a;两个无穷小的和是无穷小 定理2&#xff1a;有界函数与无穷小的乘积是无穷小 极限的四则运算法则 定理3 定理4 定理5&#xff1a;极限的保序性…

实现自己的数据库二

一 前言上次数据库支持了一个测试表的插入和查询&#xff0c;但是数据全部保存到磁盘中的&#xff0c;如果程序重启后&#xff0c;数据都会全部丢了&#xff0c;所以需要持久化到磁盘上&#xff0c;像sqlite一样&#xff0c;简单的将数据库的数据保存到一个磁盘文件上。二 实现…

【BBuf的CUDA笔记】六,总结 FasterTransformer Encoder(BERT) 的cuda相关优化技巧

这里总结 FasterTransformer Encoder(BERT) 的cuda相关优化技巧 解读&#xff1a;https://github.com/NVIDIA/FasterTransformer/blob/main/docs/bert_guide.md &#xff0c;优化点解读之前是翻译了下 Faster Transformer BERT 的文档&#xff0c;然后省略了运行样例等环节&…

【Datewhale一起吃瓜 Task4】啃瓜第五章

支持向量机 任务&#xff1a;找到超平面 在样本空间中&#xff0c;找到最好的超平面把样本分开&#xff0c;即找到正中间的超平面 满足 该超平面 分开了两类该超平面 最大化支持向量间隔该超平面处于 间隔中间&#xff0c;到所有支持向量距离相等 如何找&#xff1a;表示出…