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;
}
相关推荐
hoiii1872 小时前
C# 俄罗斯方块游戏
开发语言·游戏·c#
huaqianzkh2 小时前
WinForm + DevExpress 控件的「完整继承关系」
开发语言
a***59263 小时前
C++跨平台开发:挑战与解决方案
开发语言·c++
青槿吖3 小时前
Java 集合操作:HashSet、LinkedHashSet 和 TreeSet
java·开发语言·jvm
刘联其3 小时前
Prism Region注册父子区域 子区域初始化导航没生效解决
java·开发语言
hetao17338373 小时前
2026-01-12~01-13 hetao1733837 的刷题笔记
c++·笔记·算法
Yu_Lijing3 小时前
基于C++的《Head First设计模式》笔记——外观模式
c++·笔记·设计模式
CoderCodingNo3 小时前
【GESP】C++六级考试大纲知识点梳理, (5) 动态规划与背包问题
开发语言·c++·动态规划
移幻漂流3 小时前
Lua脚本的游戏开发优势与应用开发局限:技术对比与行业实践深度解析
开发语言·junit·lua