C++类和对象(3)

目录

再谈构造函数

构造函数体赋值

初始化列表

【注意】

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

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

初始化列表

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

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成员变量

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

 引用成员变量

	int a = 10;
	int& b = a;// 创建时就初始化

const成员变量 

	const int a = 10;//correct 创建时就初始化
	const int b;//error 创建时未初始化

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

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

3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。 

class Time
{
public:
 Time(int hour = 0)
 :_hour(hour)
 {
 cout << "Time()" << endl;
 }
private:
int _hour;
};
class Date
{
public:
 Date(int day)
 {}
private:
 int _day;
 Time _t;
};
int main()
{
    Date d(1);
}

4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关 

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  1 B.程序崩溃 C.编译不通过 D.输出1  随机值

答案是D 

explicit关键字 

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

class Date
{
public:
比特就业课
上述代码可读性不是很好,用explicit修饰构造函数,将会禁止构造函数的隐式转换。
2. static成员
2.1 概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用
static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
面试题:实现一个类,计算程序中创建出了多少个类对象。
 // 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用
 // explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译
 explicit Date(int year)
 :_year(year)
 {}
 /*
 // 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具
有类型转换作用
 // 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()
{
 Date d1(2022);
 // 用一个整形变量给日期类型对象赋值
 // 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
 d1 = 2023;
 // 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转
换的作用
}

上述代码可读性不是很好,用explicit修饰构造函数,将会禁止构造函数的隐式转换。 

Static成员 

概念

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

特性

1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区

#include <iostream>
using namespace std;
class Test
{
private:
	static int _n;
};
int main()
{
	cout << sizeof(Test) << endl;
	return 0;
}

 计算Test类的大小为1,因为静态成员_n是存储在静态区的,属于整个类,也属于类的所有对象

2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明

class Test
{
private:
	static int _n;
};
// 静态成员变量的定义初始化
int Test::_n = 0;

注意:这里静态成员变量_n虽然是私有,但是我们在类外突破类域直接对其进行了访问。这是一个特例,不受访问限定符的限制,否则就没办法对静态成员变量进行定义和初始化了。 

3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问

#include<iostream>
using namespace std;
class Test
{
public:
    static int _n;
};
// 静态成员变量的定义初始化
int Test::_n = 0;
int main() {
    Test A;
    cout << "::访问:" << Test::_n << endl;
    cout << "对象.静态成员访问:" << A._n<< endl;

}

4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

class Test
{
public:
	static void Fun()
	{
		cout << _a << endl; //error不能访问非静态成员
		cout << _n << endl; //correct
	}
private:
	int _a; //非静态成员
	static int _n; //静态成员
};

含有静态成员变量的类,一般要写一个静态成员函数,用于访问静态成员变量 

5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

当静态成员变量设置为private时,尽管我们突破了类域,也不能对其进行访问

【问题】

1. 静态成员函数可以调用非静态成员函数吗?

:不可以。因为非静态成员函数的第一个形参默认为this指针,而静态成员函数中没有this指针,故静态成员函数不可调用非静态成员函数。
 

2. 非静态成员函数可以调用类的静态成员函数吗?

:可以。因为静态成员函数和非静态成员函数都在类中,在类中不受访问限定符的限制。

友元 

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

友元函数

问题:现在尝试去重载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;
};

 友元关系是单向的,不具有交换性。

比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

友元关系不能传递

如果C是B的友元, B是A的友元,则不能说明C时A的友元。

友元关系不能继承

 

内部类 

概念

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。

注意
 1.此时的内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象区调用内部类。
 2、外部类对内部类没有任何优越的访问权限。
 3、内部类就是外部类的友元类,即内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。 

特性: 

1. 内部类可以定义在外部类的public、protected、private都是可以的。

2.注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。

3. sizeof(外部类)=外部类,和内部类没有任何关系。

#include <iostream>
using namespace std;
class A //外部类
{
public:
	class B //内部类
	{
	private:
		int _b;
	};
private:
	int _a;
};
int main()
{
	cout << sizeof(A) << endl; //外部类的大小
	return 0;
}

匿名对象

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 aa1();
 // 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
 // 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
 A();
 A aa2(2);
 // 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说
 Solution().Sum_Solution(10);
 return 0;
}

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

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还 是非常有用的。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

后端打印不了trace等级的日志?-SpringBoot日志打印-Slf4j

在调用log变量的方法来输出日志时&#xff0c;有以上5个级别对应的方法&#xff0c;从不太重要&#xff0c;到非常重要 调用不同的方法&#xff0c;就会输出不同级别的日志。 trace&#xff1a;跟踪信息debug&#xff1a;调试信息info&#xff1a;一般信息warn&#xff1a;警告…

炒股怎么做杠杆?安全正规的融资融券了解一下!

加杠杆炒股是指放大投资资金进行股票交易&#xff0c;比如自有资金100万&#xff0c;向证券公司融资100万&#xff0c;那么投资者炒股的本金就有200万。当股市行情好的时候可以放大我们的收益&#xff01; 目前我国股票加杠杆通过融资融券来实现&#xff0c;这个是唯一安全正规…

网络协议 - DNS 相关详解

网络协议 - DNS 相关详解 DNS简介域名层级结构域名服务器 DNS 解析流程为什么DNS通常基于UDP DNS 查询dig 查询host查询nslookup查询whois查询在线工具查询 DNS 调度原理地理位置调度不准确规则变更生效时间不确定高可用 DNS 安全相关什么是DNS劫持什么是DNS污染为什么要DNS流量…

电源适配器老化测试方法分享 电源测试系统助力老化测试

电源适配器老化测试是指对适配器进行高负荷、长时间的运行测试&#xff0c;从而评估电源适配器的性能、稳定性和可靠性。通过老化测试可以检测电源适配器长时间的使用情况&#xff0c;从而指导适配器的设计和研发&#xff0c;提高电源适配器的质量。由于老化测试要求长时间运行…

innovus:ccopt_design流程

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 ccopt完整的流程包括如下几个步骤&#xff1a; spec文件可以只创建一次&#xff0c;无需多次创建。 1&#xff09;clustering阶段 set_ccopt_property balance_mode cluster …

camunda流程引擎——Java集成Camunda(上)(笔记)

目录 一、以一个处理流程开始1.1 后端1.2 前端1.3 执行 二、Camunda的补充2.1 使用方式2.2 可视化平台的Cockpit2.3 流程相关数据2.4 表介绍2.5 前端集成Modeler 三、用Java集成Camunda3.1 集成配置3.2 自动部署3.2.1 修改process.xml位置3.2.2 多进程引擎配置与多租户 3.3 历史…

《Java 核心技术·卷I (第11版)》笔记

文章目录 第1章 Java程序设计概述1.1 Java程序设计平台1.2 Java “白皮书” 的关键术语1.2.1 简单性1.2.2 面向对象1.2.3 分布式1.2.4 健壮性1.2.5 安全性1.2.6 体系结构中立1.2.7 可移植性1.2.8 解释型1.2.9 高性能1.2.10 多线程1.2.11 动态性 1.3 Java applet 与 Internet1.4…

线性回归在数据库中的应用

简介 今天看到微信群有人问&#xff0c;如何知道数据库一年的磁盘增量&#xff1f;如果没有研究过统计学&#xff0c;IT人员对于这个问题就只能靠经验了去断定了。没经验的往往都是回复扩容越大越好。当然未来的事情我们是无法预料的。本博主就通过简单的线性回归做一个计算&am…

XS9922B-国产cvi协议,满足国内车载视频传输领域国产化降本需求

XS9922B 是一款 4 通道模拟复合视频解码芯片&#xff0c;支持 HDCCTV 高清协议和 CVBS 标 清协议&#xff0c;视频制式支持 720P/1080P 高清制式和 960H/D1 标清制式。芯片将接收到的高清 模拟复合视频信号经过模数转化&#xff0c;视频解码以及 2D 图像处理之后&#xff0c;转…

CVE-2023-49371|RuoYi 若依后台管理系统存在SQL注入漏洞

0x00 前言 RuoYi是一个后台管理系统&#xff0c;基于经典技术组合&#xff08;Spring Boot、Apache Shiro、MyBatis、Thymeleaf&#xff09;主要目的让开发者注重专注业务&#xff0c;降低技术难度&#xff0c;从而节省人力成本&#xff0c;缩短项目周期&#xff0c;提高软件安…

相信99%的朋友都没有注意到的数据库时间类型的问题

文章目录 创建表SQL实例小测试知识点小测试可以怎样处理只有查询有问题吗&#xff1f;MySQL时间 很多时候&#xff0c;程序运行起来没有问题&#xff0c;并不代表程序就精确&#xff0c;例如创建时间多一秒少一秒这种事情&#xff0c;很多时候是没有人注意到这个问题。 当然&am…

C++/语法@初始化列表

目录 初始化列表特征疑惑区别必在初始化列表中初始化的三种成员变量1、引用成员变量程序例子&#xff1a;运行结果&#xff1a; 2、const成员变量程序例子&#xff1a;运行结果&#xff1a; 3、自定义类型成员&#xff08;没有默认构造函数的类&#xff09;程序例子&#xff1a…

【LeetCode:2132. 用邮票贴满网格图 | 二维前缀和 + 二维差分和】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【docker 】Dockerfile指令学习

学习文档地址 上篇文章&#xff1a;【docker 】基于Dockerfile创建镜像 Dockerfile指令文档地址 .dockerignore 文件 Dockerfile指令 常见的指令 Dockerfile 指令说明FROM指定基础镜像&#xff0c;用于后续的指令构建。MAINTAINER指定Dockerfile的作者/维护者。&#xff…

伦敦金投资者的本质其实是风险管理者

长期在市场中可以稳定盈利的投资者&#xff0c;他们的秘密是什么&#xff1f;很多人以为&#xff0c;肯定是他有别人所没有的交易策略。其实并不是&#xff0c;交易技术固然很重要&#xff0c;但在持续盈利的问题上&#xff0c;技术所占的重要性是次要的&#xff0c;而主要的是…

Django 模型操作 - 多对多(九)

一、多对多关联管理器(对象调用) 前提&#xff1a;多对多&#xff08;双向均有关联管理器&#xff09;一对多&#xff08;只有多的那个类的对象有关联管理器&#xff0c;即反向才有&#xff09; 语法格式&#xff1a;正向&#xff1a;属性名反向&#xff1a;小写类名加 _set注意…

H3C ER G2系列路由器信息泄露漏洞

H3C ER G2系列路由器信息泄露漏洞 免责声明漏洞描述漏洞影响漏洞危害漏洞页面漏洞复现1. 构造poc2. 发生数据包&#xff0c;获取密码3. 登录系统 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地提高网络安全意识和技术水平。 任何人不得利用该文章…

详解—【C++】lambda表达式

目录 前言 一、lambda表达式 二、lambda表达式语法 2.1. lambda表达式各部分说明 2.2. 捕获列表说明 三、函数对象与lambda表达式 前言 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法。 #include <algorithm> #i…

Java EE 多线程之多线程案例

文章目录 1. 多线程案例1.1 单例模式1.1.1 饿汉模式1.1.2 懒汉模式1.1.3 多线程下的单例模式 1.2 阻塞队列1.2.1 阻塞队列定义1.2.2 生产者消费者模型的意义1.2.4 标准库中的阻塞队列1.2.5 实现阻塞队列1.2.6 用阻塞队列实现生产者消费者模型 1.3 实现定时器1.3.1 标准库中的定…

V2X在做什么?连接未来智能出行的车联网(上)

来源&#xff1a;德思特测试测量 德思特分享丨V2X在做什么&#xff1f;连接未来智能出行的车联网&#xff08;上&#xff09; 原文链接&#xff1a;德思特分享 | V2X在做什么&#xff1f;连接未来智能出行的车联网&#xff08;上&#xff09; 欢迎关注虹科&#xff0c;为您提…