当前位置: 首页 > news >正文

C++类和对象(3)

构造函数

初始化列表

初始化列表是成员变量定义的地方,不论是否显式地写出来,类中所有成员变量都会按照成员变量声明的顺序,经过初始化列表进行初始化。并秉持C++的风格:

(1)如果内置类型没有写进初始化列表:一般编译器不做处理(随机值);

(2)如果自定义类型没有写进初始化列表:编译器会调用这个自定义类型的默认构造函数。

class A {
public:A():_a(0),_ai(nullptr){}private:int _a;		// 初始化为 0int* _ai;	// 初始化为 空指针B b;		// 调用 B 类的默认构造
};

另外,从 C++11 开始,支持在类成员变量的声明处给缺省值。如果没有在初始化列表中显式初始化,那么默认使用这个成员级的缺省值进行初始化。如果构造函数中也给了缺省值,那么优先使用构造函数中的缺省值。

有 3 类成员变量,必须在被定义时就初始化,也就是需要在初始化列表初始化:

(1)const 成员变量;

(2)引用;

(3)没有默认构造的自定义类型(构造函数是带参且没有缺省参数)。

class B {
public:B(char* c):_arr(c){}
private:char* _arr;
};class A {
public:A(int ref, char* a):_a(0),_refi(ref),_b(a){}
private:const int _a;int& _refi;B _b;
};

explicit 关键字

C++98支持单参数构造函数的隐式类型转换,C++11支持多参数构造函数的隐式类型转换。

class A
{
public:A(int a = 6):_a(a){}
private:int _a;
};class B
{
public:B(int b = 6, char c = '\0'):_b(b),_c(c){}
private:int _b;char _c;
};int main()
{A a(2);	// 正常初始化 A 类对象A a1 = 1;	// 单参数构造函数的隐式类型转换B b1(1, 'a');	// 正常初始化 B 类对象B b2 = { 6, 'b' };	// 多参数构造函数的隐式类型转换return 0;
}

这种隐式类型转换的过程是:使用参数调用该类的构造函数生成一个临时对象,再用该临时对象拷贝构造 a1 / b2 对象;但一般编译器会优化,将 构造+拷贝构造 的过程优化为 使用参数直接构造对象。

如果需要禁止这种隐式类型转换,使用 explicit 修饰构造函数即可:

class A
{
public:explicit A(int a = 6):_a(a){}
private:int _a;
};

匿名对象

	A a1(1);	// 有名对象A(2);	// 匿名对象const A& aa = A(3);	// 对匿名对象的 const 引用

匿名对象的生命周期只在这一行语句。但如果被 const 引用,生命周期会延长,直到这个引用的生命周期也结束。

如果是为了调用某个类中的成员函数,而创建的对象;那么可以使用匿名对象调用,简化代码。

	A().Print();

static 成员

使用 static 修饰的成员变量称之静态成员变量;修饰的成员函数称之为静态成员函数。

static 修饰的类成员属于这个类的所有对象,受访问限定符的限制,存储在静态区,不会在对象中开辟空间存储。

静态成员变量

必须在类外定义,类中只是声明:

class A
{
public:
private:static int _a;
};
int A::_a = 0;

静态成员函数

静态成员函数没有隐藏的 this 指针,就不能得知这个非静态成员来自哪个对象,所以不能访问任何非静态成员:

class A
{
public:static char* Getc(){return _c;	// 报错,不能访问非静态成员}void Print(){cout << _a << endl;}static void GetPrint(){Print();    // 报错}private:static int _a;char* _c = nullptr;
};
int A::_a = 0;

但是非静态的成员函数可以调用类的静态成员函数,因为静态成员函数又不需要传 this 指针...

class A
{
public:void Print(){GetPrint();    // 非静态成员函数,可调用静态成员函数}static void GetPrint(){cout << _a << endl;}private:static int _a;char* _c = nullptr;
};
int A::_a = 0;

友元

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

友元函数

现在尝试重载 A 类的 << 流插入运算符:

class A
{
public:A(char c = 'a'):_c(c),_a(0){}ostream& operator<<(ostream& out)	// 重载流插入{cout << _c << endl;return out;}
private:char _c;size_t _a;
};int main()
{A a;//cout << a;	// 报错a << cout;    // a.operator<<(cout)return 0;
}

发现,由于 << 作为 A 类的成员函数进行重载,在参数列表中已经隐含了第一个参数 对象的 this 指针,所以调用时,a 对象在左,cout 在右;但这样显然不符合平时标准调用的逻辑;

所以需要调换参数列表中的 this 和 out,那么只能在类外实现:

ostream& operator<<(ostream& out, const A& a)	// 重载流插入
{cout << a._c << endl;	// 类外访问私有成员 报错return out;
}

但类外不能直接访问私有成员,提供两种方法访问:

(1)使用友元函数

将 << 运算符重载函数声明为 A 类的友元函数,友元函数可以直接访问 A 类的私有成员:

class A
{friend ostream& operator<<(ostream& out, const A& a);// friend关键字
public:A(char c = 'a'):_c(c),_a(0){}
private:char _c;size_t _a;
};

(2)提供成员函数间接获取

class A
{
public:A(char c = 'a'):_c(c),_a(0){}const char& Get_c() const	// 注意 const 修饰{return _c;}
private:char _c;size_t _a;
};ostream& operator<<(ostream& out, const A& a)	// 重载流插入
{cout << a.Get_c() << endl;	// 类外访问私有成员return out;
}

友元类

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

注意:友元关系是单向的,且不会传递,不能继承。

class A
{friend class B;    // 友元类
public:......略
};class B
{
public:void Print(){cout << A()._c << endl;	// 使用匿名对象访问了A类的私有成员}
};

内部类

定义在另一个类中的类,就是内部类。

内部类是一个独立的类,不属于外部类;且外部类也没有对内部类成员的访问权限

内部类是外部类的友元类,可通过外部类的对象参数,访问外部类的成员;

内部类可以直接访问外部类的 static 成员,而不需要突破 类域 的限制(因为本身就已经在类域中了)。

大小:计算外部类的大小时,与内部类无关。

class A
{
public:A(char c = 'a'):_c(c),_a(0){}class B	//内部类{public:void Print(){cout << A()._c << endl;	// 无需声明友元类,即可访问A的成员}};
private:char _c;size_t _a;
};
http://www.dtcms.com/a/291789.html

相关文章:

  • 从零搭建 OpenCV 项目(新手向)--第一天初识OpenCV与图像基础
  • MCP:Cline+DeepSeek在VSCode上配置
  • TDengine 计算百分位函数使用手册
  • .net web 中如何优雅地使用 redis?
  • MFC类Qt的自动布局框架
  • 景区负氧离子监测设备:守护清新,赋能旅游
  • 【kubernetes】-2 K8S的资源管理
  • 学习笔记-关于中华心法问答系统的环境配置和源代码理解
  • 基于Vue+ElementUI的借还款利息计算器
  • Java 动态导出 Word 登记表:多人员、分页、动态表格的最佳实践
  • SpringBoot集成PDFBox实现PDF导出(表格导出、分页页码、电子签章与数字签名)
  • RISC-V基金会Datacenter SIG月会圆满举办,探讨RAS、PMU性能分析实践和经验
  • Rust实战:决策树与随机森林实现
  • 【vscode】vscode中python虚拟环境的创建
  • 激光雷达-自动驾驶的“三维感知中枢“
  • IntelliJ IDEA (2024.3.1)优雅导入 Maven 项目的两种方式详解
  • 【Java企业级开发】(六)Java框架技术-Maven和MyBatis
  • Docker容器 介绍
  • Maven 环境配置全攻略:从入门到实战
  • Kafka灰度方案
  • 两个android,一个客户端一个服务器端
  • 【菜狗学聚类】时间序列聚类主要方法—20250722
  • zmaiFy来说软字幕和硬字幕有什么优缺点?
  • 【LINUX】CentOS Stream 9 手动配置网络
  • [hot 100]两数之和-Python3-Hash Table
  • 归一化 vs 标准化:数据预处理终极指南
  • Matplotlib绘制各种图参考
  • 力扣刷题 -- 101.对称二叉树
  • JAVA API (三):从基础爬虫构建到带条件数据提取 —— 详解 URL、正则与爬取策略
  • 【网工】学而思:生成树协议STP原理与应用