[C++]类和对象【下】

news2024/11/17 5:48:18

🥁作者: 华丞臧
📕​​​​专栏:【C++】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。

推荐一款刷题网站 👉LeetCode


文章目录

  • 再谈构造函数
    • 构造函数整体赋值
    • 初始化列表
    • explicit关键字
  • static成员
    • 概念
    • 特性
  • 友元
    • 友元函数
    • 友元类
  • 内部类(了解)
  • 匿名对象
  • 拷贝对象时的一些编译器优化
  • 再次理解类和对象


再谈构造函数

构造函数整体赋值

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。如下:

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

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

初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或者表达式。
那么对象的每个成员什么时候定义初始化呢?

初始化列表,每个成员变量都要走初始化列表,就算不显示写初始化列表,也会走初始化列表。如果不显示写初始化列表,声明处给了缺省值就会使用缺省值初始化,如果显示写了初始化列表,即使给了缺省值也不会使用。
怎么走如下

  1. 对于内置类型,优先使用初始化列表;没显示写初始化列表,有缺省值用缺省值,没有就用随机值;
  2. 自定义类型,调用它的默认构造函数,如果没有默认构造就报错。
class Date
{
public:
    Date(int year, int month, int day)
        : _year(year)
        , _month(month)
        , _day(day)
    {}
private:
    int _year;
    int _month;
    int _day;
};

注意:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次);
  2. 类中包含一下成员,必须放在初始化列表位置初始化:
    • 引用成员变量
    • const成员变量
    • 自定义类型成员(且该类没有默认构造函数时)
class A
{
public:
    A(int a)
        :_a(a)
    {}
private:
    int _a;
};
class B
{
public:
    B(int a, int ref)
        :_aobj(a)
        , _ref(ref)
        , _n(10)
    {}
private:
    A _aobj;  // 没有默认构造函数
    int& _ref;  // 引用
    const int _n; // const 
};
  1. 尽量使用初识化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化;
  2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关;
//你认为下面这段代码输出的结果如何?
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关键字

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

class Date
{
public:
    // 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
    Date(int year)
        :_year(year)
    {}
    /*
    // 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
   有类型转换作用
    Date(int year, int month = 1, int day = 1)
    : _year(year)
    , _month(month)
    , _day(day)
    {}
    */
    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
private:
    int _year;
    int _month;
    int _day;
};
void Test()
{
    // 用一个整形变量给日期类型对象赋值
   // 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
   //1和2都可以进行隐式类型转换

    Date d1(2022);
    d1 = 2023; //C++98支持
}

而上述代码中,用explicit关键字修饰构造函数再编译就会报错,这是因为explicit禁止类型转换

class Date
{
public:
 // explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
    explicit Date(int year)
        :_year(year)
    {}
    /*
    // explicit修饰构造函数,禁止类型转换
    explicit Date(int year, int month = 1, int day = 1)
    : _year(year)
    , _month(month)
    , _day(day)
    {}
    */
    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
private:
    int _year;
    int _month;
    int _day;
};
void Test()
{
    // 不管使用的是1还是2都会编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转
    //换的作用
    Date d1(2022);
    d1 = 2023;
}

程序运行错误如下:
在这里插入图片描述

static成员

概念

声明为static的类成员称为类的静态成员用static修饰的成员变量,称之为静态成员变量用static修饰的成员函数,称之为静态成员函数静态成员变量一定要在类外进行初始化
【面试题】实现一个类计算程序中创建出了多少个类对象。

class A
{
public:
	A()
	{
		++_count;
	}
	A(A& _a) 
	{
		++_count;
	}
	~A()
	{
		--_count;
	}
	static int GetCount()
	{
		return _count;
	}
private:
	static int _count;
};

int A::_count = 0;  //静态成员变量定义初始化

void test()
{
	cout << A::GetCount() << endl;
	A a1;
	A a2;
	A a3;
	cout << A::GetCount() << endl;
}

特性

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

【问题一】静态成员函数可以调用非静态成员函数吗?

静态成员函数没有隐含的this指针,不能访问任何非静态成员,并且静态成员函数属于类而不是某个对象。

【问题二】非静态成员函数可以调用类的静态成员函数吗?

非静态成员函数可以调用类的静态成员函数。

友元

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

友元函数

在日期类的学习当中,我们尝试去重载operator<<和operator>>,然后发现没办法将operator<<和operator>>重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置

this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。因此我们使用了友元函数重载了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;
};

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

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修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制;
  • 一个函数可以是多个类的友元函数;
  • 友元函数的调用与普通函数的调用原理相同。

友元类

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

  • 友元关系是单向的,不具有交换性;
  • 友元关系不能传递;
  • 友元关系不能继承。
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;
};

内部类(了解)

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

【特性】

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3. sizeof(外部类)=外部类,和内部类没有任何关系。
class A
{
private:
    static int k;
    int h;
public:
    class B // B天生就是A的友元
    {
    public:
        void foo(const A& a)
        {
            cout << k << endl;//OK
            cout << a.h << endl;//OK
        }
    };
};
int A::k = 1;
int main()
{
    A::B b;
    b.foo(A());

    return 0;
}

匿名对象

所谓匿名对象,就是没有名字的对象。

//假设有类型A
A(参数列表); //匿名对象
//假设有如下类型
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;
	}
};

那么A类型定义对象的方式如下:

int main()
{
	A aa1;
	// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义;
	//A aa1();
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数;
	A(); //匿名对象
	A aa2(2);//A aa3 = 2;
	// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景;
	Solution().Sum_Solution(10);
	return 0;
}

在这里插入图片描述

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

在传值和传返回值的过程中,一般C++编译器会做一些优化,减少对象的拷贝,这个在一些场景下是非常有用的。
注意:编译器优化只能在连续的步骤里面优化。

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 f1(A aa)
{}
A f2()
{
	A aa;
	return aa;
}
int main()
{
	// 传值传参
	A aa1;
	f1(aa1);
	cout << endl;
	// 传值返回
	f2();
	cout << endl;
	// 隐式类型,连续构造+拷贝构造->优化为直接构造
	f1(1);
	// 一个表达式中,连续构造+拷贝构造->优化为一个构造
	f1(A(2));
	cout << endl;
	// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
	A aa2 = f2();
	cout << endl;
	// 一个表达式中,连续拷贝构造+赋值重载->无法优化
	aa1 = f2();
	cout << endl;
	return 0;
}

【无优化时,如下】
在这里插入图片描述
【场景一】
一个表达式中,连续构造+拷贝构造,编译器会优化为一个构造。
在这里插入图片描述
【场景二】
一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
在这里插入图片描述
【场景三】
一个表达式中,连续拷贝构造+赋值重载->无法优化。
在这里插入图片描述

再次理解类和对象

现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:

  1. 用户先要对现实中洗衣机实体进行抽象—即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有那些功能,即对洗衣机进行抽象认知的一个过程
  2. 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中
  3. 经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象,此时计算机才能洗衣机是什么东西。
  4. 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。

在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有那些属性,那些方法,描述完成后就形成了一种新的自定义类型,才用该自定义类型就可以实例化具体的对象。

在这里插入图片描述

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

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

相关文章

结构建模设计——Solidworks软件之装配体操作基本总结一(装配体功能界面简介、插入零件操作、基本配合操作)

【系列专栏】&#xff1a;博主结合工作实践输出的&#xff0c;解决实际问题的专栏&#xff0c;朋友们看过来&#xff01; 《QT开发实战》 《嵌入式通用开发实战》 《从0到1学习嵌入式Linux开发》 《Android开发实战》 《实用硬件方案设计》 长期持续带来更多案例与技术文章分享…

漏洞猎人白夜追凶记

编者按 数字化浪潮蓬勃兴起&#xff0c;企业面临的安全挑战亦日益严峻。 腾讯安全近期将复盘2022年典型的攻击事件&#xff0c;帮助企业深入了解攻击手法和应对措施&#xff0c;完善自身安全防御体系。 本篇是第三期&#xff0c;用一个实际案例讲述了企业在面临攻击时&#xf…

【Java枚举类与注解】——一篇文章读懂枚举类与注解

文章目录2.枚举2.1概述2.2定义格式2.3枚举的特点2.4枚举的方法3.注解3.1概述3.2自定义注解3.3 元注解2.枚举 2.1概述 为了间接的表示一些固定的值&#xff0c;Java就给我们提供了枚举&#xff0c;是指将变量的值一一列出来&#xff0c;变量的值只限于列举出来的值的范围内。 …

C. Arpa’s overnight party and Mehrdad’s silent entering(构造+二分图)

Problem - 741C - Codeforces 请注意&#xff0c;阿尔帕的土地上的女孩真的很有吸引力。 阿尔帕喜欢过夜的聚会。在一次聚会中&#xff0c;迈尔达德突然出现。他看到n对朋友围坐在一张桌子旁。第i对包括一个男孩&#xff0c;坐在第i把椅子上&#xff0c;和他的女朋友&#xff…

【LeetCode每日一题】——264.丑数 II

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 动态规划 二【题目难度】 中等 三【题目编号】 264.丑数 II 四【题目描述】 给你一个整数 n …

React DAY06

复习&#xff1a; 1.React是一个用于构建用户界面的JS库——不涉及HTML/CSS。可用于创建PC网站、H5、WebApp、NativeApp。2013年中Facebook开源&#xff0c;目前最新版V18.2。 主要知识点&#xff1a; ①使用React的两种方法&#xff1a;脚本引入式、脚手架方式(create-react-…

VUE部署至Nginx

VUE部署至Nginx安装Nginx解压Nginx配置VUE文件配置Nginx启动Nginx快速停止或关闭nginx正常停止或关闭nginx配置文件nginx.conf修改重装载命令安装Nginx 网址&#xff1a;https://nginx.org/en/download.html 选择图中红框下载安装 解压Nginx 只需要解压到目录&#xff0c;无…

GitHub典藏版,腾讯T14级高级程序员亲码的分布式数据库实践,再次爆火

数据库就是要做好五件事&#xff0c;存储、事务、查询、复制和其他。而对分布式数据库来说&#xff0c;不仅要继续做这五件事&#xff0c;还要多出一件事&#xff0c;分片。在这六件事中&#xff0c;存储和其他这两件事与单体数据库差不多&#xff0c;难点就在事务、查询、复制…

论文复现丨基于ModelArts进行图像风格化绘画

摘要&#xff1a;这个 notebook 基于论文「Stylized Neural Painting, arXiv:2011.08114.」提供了最基本的「图片生成绘画」变换的可复现例子。本文分享自华为云社区《基于ModelArts进行图像风格化绘画》&#xff0c;作者&#xff1a; HWCloudAI 。 项目首页 | GitHub | 论文 …

敏捷、分散式的数据治理,该如何实现?

01 数据资产的生产和消费现状 —— 孤岛就在那里&#xff01; 在大数据时代&#xff0c;企业数据资产的生产和消费&#xff0c;实际现状大概是这样的。 一方面&#xff0c;每个业务部门都产生并存储了大量的数据。这些数据存储在不同的系统中。每个业务部门都是数据的生产者…

安卓逆向-某音乐软件

初学安卓逆向&#xff0c;如有错误请指教 某我音乐9.3.4.4版本&#xff0c;需要资源的请流言&#xff08;也可以自行去下载&#xff09; 直接将apk拖入到AndroidKiller里面(该工具自行下载) 首先去除广告 直接全局搜索KEY_EXTRA_AUTH&#xff0c;可以只在.smali文件里面搜索…

【gitlab wiki】git首次上传本地文档操作步骤

1.在gitLab中创建一个项目 2.进入本地电脑中的你要上传文件的文件目录&#xff0c;右击鼠标选择“ Git Bash Here” git命令窗口(本机电脑要安装好git) 3.在远程git项目中&#xff0c;复制出项目http地址。 4.在“ Git Bash Here” git命令窗口输入命令&#xff1a;git clone h…

Java+MYSQL基于ssm的网上出差审批与费用报销管理系统

全新的时代,新的技术推动着公司管理制度的改革,在管理层面加入了先进的科学技术做到了与时俱进,所以企业创建自己的网上出差审批与费用报销系统是迫切需要的。在新时代的背景下,传统管理方式的缺点被暴露出来,传统管理方式的不足的地方有管理及时性不够,下达一个指令以后需要层…

AuthLab权限在线靶场通关记录

AuthLab通关记录 一个在线的权限靶场&#xff1a;https://authlab.digi.ninja/ 靶场内容比较简单&#xff0c;包括了JWT以及一些基本情况的权限Bypass IP Based Auth Bypass 该关卡根据提示有一个ip在192.168.0.100-200范围里可以直接bypass 拦截请求包修改X-Forwarded-For爆…

python网络副业有哪些?以自身经历分享怎么做副业挣钱

网络副业我个人比较看好的是Python&#xff0c;至少我是真实体会到了Python做副业真香&#xff0c;疫情被关在家那段时间也没耽误赚钱&#xff0c;反而比平常赚的还多一点&#xff0c;下图是我疫情期间在家做Python副业收入的部分截图&#xff0c;那会儿我用Excel表格每天记了一…

BEPU物理引擎碰撞系统的架构与设计

前面我们讲解了如何监听物理引擎的碰撞事件, 在物理引擎内核中如何架构与设计碰撞规则,使得物理Entity与周围的物理环境产生碰撞时&#xff0c;如何灵活的控制物理碰撞&#xff0c;本节給大家详细的讲解BEPUphysicsint 物理引擎内部是如何管理与控制碰撞规则的。本文主要讲解3个…

彻底删除的文件如何恢复?误删数据恢复,四种方法就可以解决

电脑磁盘中存储了许多文件&#xff0c;我们不可避免地会误删一些文件&#xff0c;但是我们中的许多人不知道在文件被错误删除后如何恢复它们。事实上&#xff0c;误删数据恢复没有想象中那么难&#xff0c;我们自己也可以操作完成。到底是什么方法&#xff1f;接下来我们将详细…

Java项目:springboot中小医院信息管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 1. 基于SpringBoot的中小医院信息管理系统&#xff0c;实现了部分核心功能。 2. 就诊卡提供了手动和读卡两种方式录入&#xff0c;其中IC读卡器…

Simulink 自动代码生成电机控制:基于霍尔FOC模型和代码生成

目录 霍尔角度估算原理 霍尔角度估算FOC模型和仿真 代码生成软件调试 总结 霍尔角度估算原理 PMSM在定子侧以互差120电角度的位置安装3个霍尔元件Ha, Hb, Hc。当转子转动时&#xff0c;霍尔元件会产生3个相位差120电角度的高低电平信号。霍尔信号会将一个电周期划分为6个扇…

可以快速搭建的免费开源项目:直播带货、富文本笔记、思维导图、声音克隆、消息推送服务、文档协作等等

可以快速搭建的免费开源项目&#xff1a;直播带货、富文本笔记、思维导图、声音克隆、消息推送服务、文档协作等等。 01 Pure Live 一个想让直播回归纯粹的项目&#xff0c;没有礼物、粉丝团、弹窗&#xff0c;只有直播和弹幕。这是国人在GitHub上制作的一个开源的直播系统&am…