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

C++入门学习5

17.再谈构造函数

还是以日期类作为例子

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

理解前面说的那句话:

初始化列表的核心是 “初始化”,必须遵循 “每个成员仅初始化一次” 的规则,重复初始化会报错。构造函数体的核心是 “赋值”,可以对成员变量多次修改,语法上允许重复操作。

注意:const 成员、引用成员、没有默认构造函数的自定义类型成员,这些成员必须在初始化列表中初始化,而不能在构造函数体内赋值。

默认构造函数是指不需要传递任何参数就能调用的构造函数,有两种形式:

1、编译器自动生成的无参构造函数(当类中没有显式定义任何构造函数时)。

2、用户自定义的无参构造函数,或全缺省参数的构造函数(如A(int a=1))。

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

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关。如下:初始化列表的执行顺序是:先初始化 _a2,再初始化 _a1

第一步:用 _a1 初始化 _a2,但此时 _a1 还未被初始化(其值是随机的),因此 _a2 被赋予了随机值。

第二步:用参数 a=1 初始化 _a1,因此 _a1 的值是 1

explicit关键字

explicit修饰构造函数,将会禁止构造函数的隐式转换。只能进行显式转换。这可以避免一些意外的类型转换导致的逻辑错误。

class A
{
public:A(int a):_a1(a){}void print(){cout << "_a1 = " << _a1 << endl;}
private:int _a1;
};
void func(A a) 
{a.print();
}int main()
{// 正常构造和传值调用A a1(1);func(a1);// 隐式转换:将 int 类型自动转换为 A 类型func(20);  // 等价于 func(A(20)),编译器自动完成转换return 0;
}

使用explicit修饰构造函数之后,编译器自动报错

18.static成员

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

实现一个类,计算程序中创建出了多少个类对象。(如下:

class A
{
public:A() { ++_count;   // 记录创建对象个数}A(const A& t) { ++_count;   // 记录创建对象个数}~A() { --_count;    // 记录销毁后,剩余的对象个数}static int GetACount()   // 静态成员函数{ return _count; }
private:static int _count;   // 静态成员变量
};int A::_count = 0;  // 静态成员需要在类外面初始化void TestA()
{// GetACount被static 修饰后才能使用A::调用cout << A::GetACount() << endl;  // 0A a1, a2;                        // 调用构造函数创建了两个对象(a1,a2A a3(a1);                        // 调用拷贝构造函数创建了1个对象(a3cout << A::GetACount() << endl;  // 3
}
int main()
{TestA();return 0;
}

注意:

静态成员变量:_count属于类 A 本身,而非某个对象,所有 A 类型对象共享这一个变量,适合用于记录类的对象总数。

特性

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

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

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

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

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

19.友元

19.1友元函数

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

class A
{
public:friend void print(const A& a);     // 友元函数的声明// 用 explicit 修饰构造函数(禁止隐式转换)explicit A(int a):_a1(a){}private:int _a1;
};void print(const A& a)  // 友元函数的实现
{cout << "_a1 = " << a._a1 << endl;
}int main()
{A a1(1);print(a1);return 0;
}

注意:

1. 友元函数可访问类的私有和保护成员,但不是类的成员函数

2. 友元函数不能用const修饰

3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制

4. 一个函数可以是多个类的友元函数

5. 友元函数的调用与普通函数的调用原理相同

19.2 友元类

class A
{
public:friend void print(const A& a);     // 友元函数的声明friend class B; // 声明B类为A类的友元类,则在B类中就直接访问A类中的私有成员变量// 用 explicit 修饰构造函数(禁止隐式转换)explicit A(int a):_a1(a){}private:int _a1;
};class B
{// 用 explicit 修饰构造函数(禁止隐式转换)explicit B(int b):_b1(b){}private:int _b1;
};void print(const A& a)  // 友元函数的实现
{cout << "_a1 = " << a._a1 << endl;
}int main()
{A a1(1);print(a1);return 0;
}

20.内部类

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

class A
{
private:static int _a1;int _a2;
public:A(int a=2):_a2(a){ }void print()  {cout << "_a1 = " << _a1 << endl;}class B // B天生就是A的友元{public:void func(const A& a){cout << _a1 << endl;    //OKcout << a._a2 << endl;  //OK// 直接访问外部类的公有静态成员,无需Outer::或对象_a1 = 100;cout << "访问外部类私有静态成员: " << _a1 << endl;}};
};
int A::_a1 = 1;
int main()
{A::B b;   // 创建B类的对象A a1;b.func(a1);return 0;
}

特性:

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

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

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

21.匿名对象

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}void print()  {cout << "_a1 = " << _a << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{// 1A a1;a1.print();// 2A().print();  // 匿名对象的特点不用取名字,但是他的生命周期只有这一行//  当某种情况下只需要调用对象的某个函数,但是又不需要创建一个对象,//  或者这个对象除了这里需要使用其他地方不使用的时候就适合使用匿名对象return 0;
}

http://www.dtcms.com/a/325055.html

相关文章:

  • Boost.Asio io_service 与 线程 的分析
  • playwright-mcp 项目全解析:从理论到实践
  • 消息队列系统测试报告
  • Effective C++ 条款33:避免遮掩继承而来的名称
  • 企业临时文件分享方案:基于本地加密的轻量级实现
  • Unity3D游戏中如何制作空气墙
  • 动态群签名-DGS:实现抗女巫攻击
  • eBay功能升级:卖家提升流量与转化的新契机
  • 深入解析NumPy广播机制:让不同形状的数组无缝运算
  • 【MySQL——第三章 :MySQL库表操作】
  • Redis 数据类型和单线程模型补充
  • HyDE 在 RAG 知识问答助手中的应用解析
  • CentOS7安装和使用Workbench
  • AtomicStampedReference解决方案
  • 基于python/django框架的车型识别系统
  • 复现论文《基于Retinex理论和深度学习的低照度图像增强算法研究》
  • 问津集 #4:The Five-Minute Rule for the Cloud: Caching in Analytics Systems
  • windows运维
  • SF-CPI-SAP问题收集24:集成地址信息的村里字段无法页面显示问题
  • ECharts 的理解和简单应用笔记
  • 【无标题】消息队列(Message Queue)是一种**进程间通信(IPC)机制
  • 深度学习-卷积神经网络-AlexNet
  • index.d.ts 是什么?作用 + 怎么生成?
  • 糖果大冒险:公平分发的智慧挑战
  • Stagewise使用指南:从项目集成到效能跃迁的深度解析
  • 【算法题】:和为N的连续正数序列
  • AI大模型-提示词工程
  • 01 词法分析陷阱:C编程中的符号误解
  • 深度解析 Spring Boot 循环依赖:原理、源码与解决方案
  • PhotoDirector 安卓版:功能强大的照片编辑与美化应用