C++ 类和对象(完)

1. 再谈构造函数

1.1 构造函数体赋值 vs 初始化列表

在创建对象时,编译器通过调用构造函数给成员变量赋初值。 虽然在构造函数体内赋值可以让对象有初始值,但这被称为赋初值而非初始化。因为初始化只能进行一次,而函数体内可以多次赋值。

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

cpp 复制代码
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 成员变量

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

重要细节: 成员变量在类中声明的次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。

  • 建议: 尽量使用初始化列表。对于自定义类型成员,无论是否写在列表中,编译器总会先尝试在列表中初始化它。

1.3 explicit 关键字

构造函数不仅用于初始化,对于单个参数 (或多参数但第一个参数外都有默认值)的构造函数,还具有隐式类型转换的作用。

  • 隐式转换示例: `Date d1 = 2023;` 编译器会用 2023 构造一个临时对象,再赋值给 d1。
  • explicit 的作用: 在构造函数前加上 `explicit` 关键字,可以禁止这种隐式类型转换,提高代码的可读性和安全性。

2. Static 成员 (Static Members)

2.1 概念与定义

声明为 `static` 的类成员称为类的静态成员。

  • 存储: 静态成员为所有类对象所共享,不属于某个具体对象,存放在静态区。
  • 定义: 静态成员变量必须在类外定义(初始化),类中只是声明。类外定义时不加 `static` 关键字。
  • 访问: 可以通过 类名::静态成员对象.静态成员 访问。

2.2 Static 成员函数

静态成员函数没有隐藏的 this 指针。

  • 限制: 不能访问任何非静态成员(因为没有 `this` 指针)。
  • 权限: 依然受 public/protected/private 访问限定符的限制。
cpp 复制代码
class A {
public:
    A() { ++_scount; } // 每创建一个对象,计数+1
    ~A() { --_scount; }
    static int GetACount() { return _scount; } // 静态函数获取静态变量
private:
    static int _scount; // 类内声明
};

int A::_scount = 0; // 类外定义及初始化 

3. 友元 (Friends)

友元提供了一种突破封装的方式,允许外部函数或类访问某个类的私有(private)和保护(protected)成员。虽然便利,但会增加耦合度,破坏封装,不宜滥用。

3.1 友元函数 (Friend Function)

友元函数是定义在类外部的普通函数,不属于任何类,但在类内部使用 `friend` 关键字声明。

  • 典型应用: 重载 `operator<<` 和 `operator>>`。
  • 痛点: 如果将 `<<` 重载为成员函数,`this` 指针会占据第一个参数位置,导致调用变成 `d1 << cout`,非常别扭。因此必须重载为全局函数。
  • 实现: 为了让全局函数能访问类的私有成员(如 `_year`),需要将其声明为友元。
cpp 复制代码
class Date {
    // 声明友元函数,它可以访问 Date 的私有成员
    friend ostream& operator<<(ostream& _cout, const Date& d);
    friend istream& operator>>(istream& _cin, Date& d);
public:
    Date(int y, int m, int d) : _year(y), _month(m), _day(d) {}
private:
    int _year;
    int _month;
    int _day;
};

// 类外定义,无需 friend 关键字,也无需 Date:: 限定符
ostream& operator<<(ostream& _cout, const Date& d) {
    _cout << d._year << "-" << d._month << "-" << d._day;
    return _cout;
} 

3.2 友元类 (Friend Class)

如果类 B 是类 A 的友元类,则类 B 的所有成员函数都可以访问类 A 的私有成员。

  • 单向性: B 是 A 的友元,不代表 A 是 B 的友元。
  • 无传递性: C 是 B 的友元,B 是 A 的友元,不能推断出 C 是 A 的友元。
  • 不能继承: 友元关系不能被继承。

4. 内部类 (Inner Class)

4.1 概念与特性

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

  • 独立性: 内部类是一个独立的类,不属于外部类。sizeof(外部类) 不包含内部类的大小。
  • 天生友元: 内部类天生就是外部类的友元。内部类可以直接访问外部类的 static 成员、枚举等,也可以通过外部类对象访问其私有成员。但反过来不行(外部类不是内部类的友元)。
  • 访问权限: 内部类可以定义在外部类的 public、protected、private 区域。
cpp 复制代码
class A {
private:
    static int k;
    int h;
public:
    class B { // B 是 A 的内部类,也是 A 的友元
    public:
    void foo(const A& a) {
            cout << k << endl;   // OK: 直接访问外部类 static
            cout << a.h << endl; // OK: 访问外部类私有成员
        }
    };
};
int A::k = 1;

int main() {
    A::B b; // 定义内部类对象
    b.foo(A());
    return 0;
}
相关推荐
灰子学技术7 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
二十雨辰7 小时前
[python]-AI大模型
开发语言·人工智能·python
Yvonne爱编码7 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚7 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂7 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
pas1367 小时前
41-parse的实现原理&有限状态机
开发语言·前端·javascript
琹箐8 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
Monly218 小时前
Java:修改打包配置文件
java·开发语言
我命由我123458 小时前
Android 广播 - 静态注册与动态注册对广播接收器实例创建的影响
android·java·开发语言·java-ee·android studio·android-studio·android runtime
island13149 小时前
CANN ops-nn 算子库深度解析:核心算子(如激活函数、归一化)的数值精度控制与内存高效实现
开发语言·人工智能·神经网络