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

C++ 类与对象(下篇)笔记整理

C++ 类与对象(下篇)笔记整理

一、再谈构造函数

构造函数是类的核心成员函数,用于对象的初始化。本节从构造函数体赋值初始化列表explicit 关键字三个维度深入解析。

1.1 构造函数体赋值

概念

构造函数体赋值是通过在构造函数内部,使用=对成员变量进行赋值的初始化方式。其本质是:先对成员变量进行默认初始化(如 int 默认随机值、自定义类型调用默认构造),再执行赋值操作

局限性
  • 效率较低:多了 “默认初始化→赋值” 的冗余步骤,尤其对自定义类型成员,会额外触发默认构造和析构。
  • 无法初始化特殊成员:const修饰的成员、引用类型成员、无默认构造函数的自定义类型成员,无法通过构造函数体赋值初始化(这些成员必须在 “定义时” 完成初始化,而构造函数体执行时成员已默认初始化)。
示例(对比初始化列表)

cpp

运行

class Date {
private:int _year;int _month;int _day;
public:// 构造函数体赋值:先默认初始化_year/_month/_day,再赋值Date(int year = 0, int month = 1, int day = 1) {_year = year;  // 赋值操作,非初始化_month = month;_day = day;}
};

1.2 初始化列表(核心)

1.2.1 概念与语法

初始化列表是 C++ 提供的直接初始化成员变量的语法,位于构造函数参数列表后、函数体前,用:开头,成员初始化项用,分隔。其本质是:成员变量的 “定义 + 初始化” 区域,成员在此处直接完成初始化,无默认初始化步骤。

1.2.2 基础语法示例(Date 类)

cpp

运行

class Date {
private:int _year;  // 成员声明(仅占坑,无内存)int _month;int _day;
public:// 初始化列表:直接初始化成员变量(定义+赋值,开辟内存)Date(int year = 0, int month = 1, int day = 1): _year(year)  // _year直接用year初始化, _month(month), _day(day){}  // 函数体可空(无额外逻辑时)
};// 调用:创建对象时,通过初始化列表完成成员初始化
int main() {Date d1(2025, 10, 14);  // _year=2025, _month=10, _day=14Date d2;                // 使用默认参数:_year=0, _month=1, _day=1return 0;
}
1.2.3 必须使用初始化列表的 4 种场景

以下成员必须在定义时完成初始化,只能通过初始化列表实现,无法用构造函数体赋值:

场景 1:const 修饰的成员变量

const变量的特性是 “初始化后不可修改”,且必须在定义时初始化。

cpp

运行

class Test {
private:const int _n;  // const成员,必须定义时初始化
public:// 必须用初始化列表初始化_nTest(int n) : _n(n) {}  // 正确:_n在初始化列表定义并赋值// Test(int n) { _n = n; }  // 错误:_n已默认初始化(随机值),赋值时冲突
};
场景 2:引用类型成员

引用的特性是 “必须在定义时绑定到一个对象”,且绑定后不可更改指向。

cpp

运行

class Test {
private:int& _ref;  // 引用成员,必须定义时绑定
public:// 必须用初始化列表绑定_ref到参数xTest(int& x) : _ref(x) {}  // 正确:_ref绑定到x// Test(int& x) { _ref = x; }  // 错误:_ref已默认初始化(悬空引用),赋值无效
};
场景 3:无默认构造函数的自定义类型成员

若成员是自定义类型(如类 A),且该类没有默认构造函数(即必须传参才能构造),则必须在初始化列表中给该成员传递构造参数。

cpp

运行

// 类A:无默认构造函数(只有带参构造)
class A {
private:int _a;
public:A(int a) : _a(a) {}  // 带参构造,无默认构造
};// 类B:包含A类型成员,必须用初始化列表初始化_aobj
class B {
private:A _aobj;  // A无默认构造,必须传参初始化
public:// 初始化列表:给_aobj传递构造参数1B() : _aobj(1) {}  // 正确:_aobj用A(1)初始化// B() { _aobj = A(1); }  // 错误:_aobj需先默认构造(无默认构造,编译报错)
};
场景 4:派生类初始化基类(后续继承内容,提前补充)

若基类的构造函数需要参数,则派生类必须通过初始化列表将参数传递给基类构造函数,确保基类先于派生类初始化。

cpp

运行

// 基类Base:带参构造
class Base {
private:int _baseVal;
public:Base(int val) : _baseVal(val) {}
};// 派生类Derived:继承Base
class Derived : public Base {
private:int _derivedVal;
public:// 初始化列表:先给基类Base传参,再初始化派生类成员Derived(int baseVal, int derivedVal): Base(baseVal)  // 必须在此处初始化基类, _derivedVal(derivedVal){}
};
1.2.4 性能优势

对比构造函数体赋值,初始化列表减少了 “默认初始化” 步骤,直接完成成员初始化:

  • 对内置类型(int、double 等):性能差异微小,但逻辑更直接。
  • 对自定义类型(如 string、Date 等):避免了 “默认构造→赋值” 的冗余,减少构造和析构开销(例如 string 默认构造会分配空内存,赋值时需释放旧内存再分配新内存,而初始化列表直接用参数构造,无冗余操作)。
1.2.5 成员初始化顺序(关键注意点)

成员在初始化列表中的初始化顺序,不取决于列表中的书写顺序,而取决于成员在类中的声明顺序。若成员初始化存在依赖关系,必须保证声明顺序正确,否则会导致逻辑错误。

示例 1:正确声明顺序

cpp

运行

class Test {
private:int x;  // 声明顺序1:先初始化xint y;  // 声明顺序2:后初始化y
public:// 列表中y写在x前,但实际初始化顺序是x→y(按声明顺序)Test() : y(2), x(1) {}  
};

示例 2:错误声明顺序(依赖冲突)

cpp

运行

class Test {
private:int x;  // 声明顺序1:先初始化xint y;  // 声明顺序2:后初始化y
public:// 错误:y未初始化时,用y初始化x(x先初始化,y此时是随机值)Test() : x(y), y(2) {}  
};

1.3 explicit 关键字

概念

explicit用于修饰单参数构造函数(或除第一个参数外其余参数均有默认值的构造函数),作用是禁止隐式类型转换(即禁止通过 “赋值语法” 创建对象)。

隐式类型转换的原理(未用 explicit 时)

若 Date 类有单参数构造函数,编译器会允许 “Date d = 2025;” 这种语法,本质是:

  1. 隐式创建临时对象:Date tmp(2025)(用 2025 调用单参数构造)。
  2. 用临时对象拷贝构造 d:Date d(tmp)
  3. 编译器优化:省略临时对象,直接用 2025 构造 d(即Date d(2025))。

示例(未用 explicit):

cpp

运行

class Date {
private:int _year;
public:// 单参数构造函数:未用explicit,允许隐式转换Date(int year) : _year(year) {}
};int main() {Date d1(2025);  // 显式构造:正确Date d2 = 2025; // 隐式转换:允许(编译器优化为直接构造)return 0;
}
explicit 的作用(禁止隐式转换)

cpp

运行

class Date {
private:int _year;
public:// 用explicit修饰:禁止隐式类型转换explicit Date(int year) : _year(year) {}
};int main() {Date d1(2025);  // 显式构造:正确// Date d2 = 2025;  // 错误:explicit禁止隐式转换,编译报错return 0;
}
补充:explicit 与多参数构造函数(C++11 后)

若构造函数除第一个参数外,其余参数均有默认值,也属于 “可隐式转换” 的场景,需用explicit禁止:

cpp

运行

class Date {
private:int _year, _month, _day;
public:// 多参数但后两个有默认值:可隐式转换(如Date d=2025)explicit Date(int year, int month=1, int day=1): _year(year), _month(month), _day(day){}
};int main() {// Date d = 2025;  // 错误:explicit禁止Date d(2025, 10, 14);  // 正确return 0;
}

二、static 成员(类级别的成员)

static修饰的成员(变量 / 函数)属于整个类,而非某个对象,是 “类级别的成员”,所有对象共享该成员。

2.1 静态成员变量

2.1.1 概念与特性
  • 存储位置:不在对象内存中,而是在静态区(程序运行期间一直存在,程序结束后释放)。
  • 共享性:所有类对象共享同一个静态成员变量,修改一个对象的静态成员,会影响所有对象。
  • 声明与定义:
    • 声明:在类内用static修饰(如static int n;),仅占坑,不分配内存。
    • 定义:必须在类外定义(如int A::n = 0;),否则链接时会报错(未分配内存)。
  • 访问权限:受public/private/protected控制(同普通成员)。
2.1.2 示例(统计对象个数)

cpp

运行

class A {
private:// 静态成员变量声明:统计A类对象总数(所有对象共享)static int _totalCount;
public:// 默认构造:创建对象时,总数+1A() { ++_totalCount; }// 拷贝构造:创建新对象时,总数+1A(const A& a) { ++_totalCount; }// 静态成员函数:获取总数(后续讲解)static int getTotalCount() { return _totalCount; }
};// 静态成员变量类外定义并初始化(必须!否则链接报错)
int A::n = 0;// 测试
int main() {A a1;A a2(a1);// 访问静态成员函数:获取对象总数(2个)cout << "Total objects: " << A::getTotalCount() << endl;  // 输出2return 0;
}
2.1.3 类外访问控制

静态成员变量的类外访问受权限控制:

  • public:可通过 “类名::成员名” 直接访问和修改(无需对象)。

    cpp

    运行

    class Sum {
    public:static int i;  // public静态成员
    };
    int Sum::i = 0;  // 类外定义int main() {Sum::i = 10;  // 合法:public权限,类外可修改cout << Sum::i << endl;  // 输出10return 0;
    }
    
  • private/protected:类外无法直接访问(即使通过 “类名::成员名” 也会编译报错),只能通过类内的公有成员函数(如静态成员函数)访问。

    cpp

    运行

    class Sum {
    private:static int i;  // private静态成员
    public:static int getI() { return i; }  // 公有静态函数访问private成员
    };
    int Sum::i = 0;  // 类外定义(合法:定义不受访问权限限制)int main() {// Sum::i = 10;  // 错误:private权限,类外无法修改cout << Sum::getI() << endl;  // 合法:通过公有函数访问,输出0return 0;
    }
    

2.2 静态成员函数

2.2.1 概念与特性
  • 类级别函数:属于整个类,无需创建对象即可调用(通过 “类名::函数名” 调用)。
  • this指针:因不依赖具体对象,静态成员函数内部没有this指针this指向当前对象)。
  • 访问限制:
    • 只能访问静态成员(静态变量 / 静态函数):非静态成员属于对象,需this指针访问,而静态函数无this
    • 不能访问非静态成员:若需访问,必须显式传递对象的指针 / 引用。
2.2.2 示例

cpp

运行

class A {
private:int _a;          // 非静态成员(属于对象)static int _n;   // 静态成员(属于类)
public:A(int a) : _a(a) {}// 静态成员函数:只能访问静态成员_nstatic int getN() { return _n; }// 静态成员函数:显式传递对象,访问非静态成员_astatic int getA(A& obj) { return obj._a; }  // 正确:通过对象引用访问
};// 静态成员变量类外定义
int A::n = 100;// 测试
int main() {A a(20);// 调用静态函数:无需对象,类名直接调用cout << "A::_n = " << A::getN() << endl;    // 输出100// 调用静态函数:传递对象,访问非静态成员cout << "a._a = " << A::getA(a) << endl;    // 输出20return 0;
}

2.3 常见问题

问题 1:静态成员函数可以调用非静态成员函数吗?

不能直接调用。原因:

  • 非静态成员函数隐含this指针,需要绑定具体对象才能调用。
  • 静态成员函数无this指针,无法确定调用哪个对象的非静态成员函数。
  • 解决方案:显式传递对象的指针 / 引用,让非静态函数绑定对象。

    cpp

    运行

    class A {
    private:int _a;void nonStaticFunc() { cout << _a << endl; }  // 非静态函数
    public:A(int a) : _a(a) {}// 静态函数:显式传递对象,调用非静态函数static void staticFunc(A& obj) {obj.nonStaticFunc();  // 正确:通过对象调用}
    };
    
问题 2:非静态成员函数可以调用静态成员函数吗?

可以直接调用。原因:

  • 非静态成员函数有this指针(绑定对象),但静态成员函数属于类,不依赖对象。
  • 非静态函数调用静态函数时,无需额外传递对象,直接调用即可(相当于调用 “类共有的功能”)。

    cpp

    运行

    class A {
    private:static int _n;static void staticFunc() { cout << _n << endl; }  // 静态函数
    public:int _a;// 非静态函数:直接调用静态函数void nonStaticFunc() {staticFunc();  // 正确:无需类名,类内可直接调用}
    };
    int A::n = 100;
    

三、C++11 成员初始化新玩法(类内缺省值)

C++11 允许在类内声明非静态成员变量时,直接设置缺省值,简化默认初始化逻辑(无需在默认构造函数中重复写默认值)。

3.1 特性与规则

  • 适用范围:仅非静态成员变量(静态成员变量需类外定义,不能类内缺省)。
  • 覆盖规则:若构造函数的初始化列表显式初始化该成员,则覆盖类内缺省值;若未显式初始化,则使用类内缺省值。
  • 本质:类内缺省值是 “默认初始化的备选值”,仅在成员未被显式初始化时生效。

3.2 示例

cpp

运行

class Date {
private:// C++11:非静态成员类内缺省值(声明时设置)int _year = 0;    // 默认年:0int _month = 1;   // 默认月:1int _day = 1;     // 默认日:1
public:// 1. 默认构造:未显式初始化成员,使用类内缺省值Date() {}// 2. 带参构造:初始化列表显式赋值,覆盖缺省值Date(int year, int month, int day): _year(year)  // 覆盖缺省值0, _month(month) // 覆盖缺省值1, _day(day)     // 覆盖缺省值1{}// 3. 部分显式初始化:未显式的成员用缺省值Date(int year): _year(year)  // 显式初始化_year{}  // _month=1(缺省),_day=1(缺省)// 打印日期void Print() {cout << _year << "/" << _month << "/" << _day << endl;}
};// 测试
int main() {Date d1;          // 调用默认构造:0/1/1Date d2(2025);    // 部分显式:2025/1/1Date d3(2025,10,14); // 全显式:2025/10/14d1.Print();d2.Print();d3.Print();return 0;
}

3.3 补充:与静态成员的区别

静态成员变量不能类内缺省初始化,必须在类外定义时赋值:

cpp

运行

class A {
public:// static int _n = 0;  // 错误:静态成员不能类内缺省static int _n;  // 声明
};
int A::n = 0;  // 类外定义并初始化(正确)

四、友元(突破封装的特殊机制)

友元是 C++ 提供的 “打破封装” 的机制,允许外部的函数 / 类直接访问当前类的private/protected成员。但友元会增加代码耦合度,破坏封装性,需谨慎使用。

4.1 友元函数

4.1.1 概念与特性
  • 定义:在类内用friend声明的全局函数(或其他类的成员函数),不是当前类的成员函数。
  • 权限:可直接访问当前类的private/protected成员(无需通过公有接口)。
  • 声明位置:类内的public/private/protected区域均可(不影响友元权限)。
  • 传递性:友元关系不可传递(A 是 B 的友元,B 是 C 的友元,A 不是 C 的友元)。
4.1.2 典型应用:重载operator<<(输出运算符)

若将operator<<重载为类的成员函数,会导致左操作数必须是类对象(因成员函数隐含this指针),不符合 “cout << 对象” 的常规用法。此时需将operator<<重载为全局函数,并声明为类的友元。

示例:

cpp

运行

#include <iostream>
using namespace std;class Date {
private:int _year = 0;int _month = 1;int _day = 1;
public:Date(int year=0, int month=1, int day=1): _year(year), _month(month), _day(day){}// 友元函数声明:允许operator<<访问private成员friend ostream& operator<<(ostream& out, const Date& d);
};// 友元函数定义(全局函数):重载operator<<
ostream& operator<<(ostream& out, const Date& d) {// 直接访问Date的private成员:_year/_month/_dayout << d._year << "/" << d._month << "/" << d._day;return out;  // 返回out,支持连续输出(如cout << d1 << d2)
}// 测试
int main() {Date d(2025, 10, 14);cout << "Date: " << d << endl;  // 输出:Date: 2025/10/14Date d2(2025, 12, 31);cout << d << " to " << d2 << endl;  // 连续输出:2025/10/14 to 2025/12/31return 0;
}
4.1.3 补充:友元函数与成员函数的区别
特性友元函数成员函数
是否属于类否(全局函数)
是否有this指针有(非静态成员函数)
调用方式直接调用(如func(d)对象调用(如d.func()
访问成员的方式需显式传递对象隐式通过this访问

4.2 友元类

4.2.1 概念与特性
  • 定义:若类 A 是类 B 的友元,则类 A 的所有成员函数(包括普通和静态成员函数)均可直接访问类 B 的private/protected成员。
  • 单向性:A 是 B 的友元,不代表 B 是 A 的友元(B 无法访问 A 的私有成员)。
  • 不可传递:A 是 B 的友元,B 是 C 的友元,不代表 A 是 C 的友元。
  • 不可继承:友元关系不能被派生类继承(A 是 B 的友元,B 的派生类 C 不是 A 的友元)。
4.2.2 示例

cpp

运行

class B {
private:int _bVal = 100;// 友元类声明:A是B的友元,A的所有成员函数可访问B的private成员friend class A;
};class A {
public:// A的成员函数:访问B的private成员_bValvoid printBVal(B& b) {cout << "B::_bVal = " << b._bVal << endl;  // 正确:友元类权限}
};// 测试
int main() {A a;B b;a.printBVal(b);  // 输出:B::_bVal = 100return 0;
}
4.2.3 注意事项
  • 友元类会显著增加类间耦合度,仅在 “两个类高度关联且必须共享细节” 时使用(如容器类与迭代器类)。
  • 避免滥用:优先通过公有接口(如Get/Set函数)访问成员,而非直接使用友元。

五、内部类(类内定义的类)

内部类是在一个类的内部定义的另一个类,属于 “嵌套类”,本质是独立的类,与外部类仅存在 “语法上的嵌套关系”。

5.1 概念与特性

  • 独立性:内部类是独立的类,编译时会生成单独的类结构,sizeof(外部类)不包含内部类的大小(内部类的对象不依赖外部类对象)。
  • 友元关系:内部类默认是外部类的友元(内部类的成员函数可访问外部类的private/protected成员,包括静态和非静态成员)。
  • 访问规则:
    • 内部类访问外部类成员:
      • 静态成员:无需外部类对象,直接通过 “外部类名::静态成员” 访问。
      • 非静态成员:需显式传递外部类对象(通过指针 / 引用)。
    • 外部类访问内部类成员:需通过内部类对象(内部类不是外部类的友元,外部类无法直接访问内部类的私有成员)。
  • 作用域:内部类的作用域在外部类内,创建内部类对象时需指定 “外部类名::内部类名”(类外创建时)。

5.2 示例

cpp

运行

class Outer {
private:int _outerNonStatic = 20;  // 外部类非静态成员static int _outerStatic = 100;  // 外部类静态成员
public:// 内部类定义(作用域在Outer内)class Inner {private:int _innerVal = 5;  // 内部类私有成员public:// 内部类成员函数:访问外部类成员void accessOuter(Outer& out) {// 访问外部类静态成员:无需对象cout << "Outer::_outerStatic = " << Outer::_outerStatic << endl;// 访问外部类非静态成员:需外部类对象cout << "Outer::_outerNonStatic = " << out._outerNonStatic << endl;}// 内部类成员函数:访问自身成员int getInnerVal() { return _innerVal; }};
};// 外部类静态成员类外定义(若未类内初始化)
// int Outer::_outerStatic = 100;// 测试
int main() {// 1. 创建内部类对象:需指定外部类域(Outer::Inner)Outer::Inner innerObj;// 2. 内部类访问外部类成员Outer outerObj;innerObj.accessOuter(outerObj);  // 输出:100 和 20// 3. 外部类访问内部类成员:需通过内部类对象(调用公有接口)cout << "Inner::_innerVal = " << innerObj.getInnerVal() << endl;  // 输出5// 4. 外部类大小:仅包含_outerNonStatic(4字节),不包含Innercout << "sizeof(Outer) = " << sizeof(Outer) << endl;  // 输出4return 0;
}

5.3 补充:内部类与友元的区别

特性内部类友元类
关系性质语法嵌套(独立类)权限共享(独立类)
友元关系内部类默认是外部类的友元需显式声明友元关系
外部类访问权限需通过内部类对象(无友元)需显式声明才有权限
作用域内部类作用域在外部类内友元类作用域独立

六、练习题(综合应用)

题目:设计一个计数器类Counter,统计对象的 “总创建次数” 和 “当前存活次数”

要求:

  1. 总创建次数:所有对象(包括默认构造和拷贝构造创建的)的总数。
  2. 当前存活次数:当前未被析构的对象数(创建时 + 1,析构时 - 1)。
  3. 提供静态成员函数获取两个次数。

实现代码与注释

cpp

运行

#include <iostream>
using namespace std;class Counter {
private:// 静态成员变量:总创建次数(所有对象共享)static int _totalCreated;// 静态成员变量:当前存活次数(所有对象共享)static int _currentAlive;public:// 1. 默认构造:创建对象,总次数+1,存活次数+1Counter() {++_totalCreated;++_currentAlive;cout << "Counter default constructed. ";cout << "Total: " << _totalCreated << ", Alive: " << _currentAlive << endl;}// 2. 拷贝构造:创建新对象,总次数+1,存活次数+1Counter(const Counter&) {++_totalCreated;++_currentAlive;cout << "Counter copy constructed. ";cout << "Total: " << _totalCreated << ", Alive: " << _currentAlive << endl;}// 3. 析构函数:销毁对象,存活次数-1~Counter() {--_currentAlive;cout << "Counter destructed. ";cout << "Total: " << _totalCreated << ", Alive: " << _currentAlive << endl;}// 4. 静态成员函数:获取总创建次数static int getTotalCreated() {return _totalCreated;}// 5. 静态成员函数:获取当前存活次数static int getCurrentAlive() {return _currentAlive;}
};// 静态成员变量类外定义并初始化(必须!)
int Counter::_totalCreated = 0;
int Counter::_currentAlive = 0;// 测试逻辑
int main() {cout << "=== Main start ===" << endl;// 创建对象c1(默认构造)Counter c1;{// 局部作用域:创建c2(拷贝构造c1)Counter c2(c1);cout << "=== Inner scope end ===" << endl;}  // c2析构(局部作用域结束)// 创建c3(默认构造)Counter c3;cout << "=== Main end ===" << endl;// c1、c3析构(main函数结束)return 0;
}

输出结果与分析

plaintext

=== Main start ===
Counter default constructed. Total: 1, Alive: 1
Counter copy constructed. Total: 2, Alive: 2
=== Inner scope end ===
Counter destructed. Total: 2, Alive: 1
Counter default constructed. Total: 3, Alive: 2
=== Main end ===
Counter destructed. Total: 3, Alive: 1
Counter destructed. Total: 3, Alive: 0
  • 总创建次数最终为 3(c1、c2、c3)。
  • 存活次数随对象创建 / 析构动态变化,main 结束后为 0(所有对象析构)。

七、再次理解封装

封装是面向对象三大特性(封装、继承、多态)的基础,核心是 “隐藏内部细节,暴露必要接口”。

7.1 封装的本质

  • 数据与行为绑定:将对象的 “属性(成员变量)” 和 “行为(成员函数)” 封装在一个类中,模拟现实世界中 “事物的属性与功能一体” 的特点(如 “日期” 的属性是年 / 月 / 日,行为是打印、判断合法性)。
  • 访问控制:通过public/private/protected控制成员的访问权限:
    • private:隐藏内部细节(如成员变量、辅助函数),仅类内可访问,防止外部随意修改。
    • public:暴露必要接口(如构造函数、PrintSetDate),外部通过接口与类交互,无需关心内部实现。
    • protected:用于继承(后续讲解),派生类可访问,外部不可访问。

7.2 封装的意义

7.2.1 工程维护角度(低耦合、高内聚)
  • 低耦合:类的内部逻辑修改时,只要对外接口(public成员)不变,外部代码无需修改(如 Date 类内部增加 “判断日期合法性” 的逻辑,外部调用Date(2025,13,1)时会报错,但调用方式不变)。
  • 高内聚:类的功能集中(如 Date 类仅处理日期相关逻辑),避免功能分散导致的维护困难。
7.2.2 数据安全角度
  • 防止非法修改:通过private隐藏成员变量,外部无法直接修改,需通过公有接口(如SetYear(int year))修改,接口中可增加合法性校验(如year不能小于 0)。

    cpp

    运行

    class Date {
    private:int _year;// 私有辅助函数:判断年份合法性bool isYearValid(int year) { return year >= 0; }
    public:// 公有接口:设置年份,带合法性校验void SetYear(int year) {if (isYearValid(year)) {_year = year;} else {cout << "Invalid year!" << endl;}}
    };
    

八、再次理解面向对象

面向对象(OOP)的核心是 “以对象为中心,模拟现实世界”,通过类与对象的机制,将复杂问题拆解为多个 “对象间的交互”。

8.1 类与对象的关系

  • 类是 “模板”:类是对一类事物的抽象描述(如 “日期” 类描述了所有日期的共同属性和行为),无具体数据,不占用内存。
  • 对象是 “实例”:对象是类的具体实例(如 “2025 年 10 月 14 日” 是 Date 类的一个对象),有具体数据,占用内存。
  • 类比:类是 “汽车设计图”,对象是 “根据设计图造的具体汽车”。

8.2 面向对象三大特性的关系

  1. 封装是基础:通过封装隐藏细节、暴露接口,为继承和多态提供安全的基础。
  2. 继承是复用:基于现有类(基类)创建新类(派生类),复用基类的代码,减少冗余(如基于 Date 类创建 DateTime 类,增加 “时 / 分 / 秒” 属性)。
  3. 多态是接口复用:不同类的对象通过统一的接口(如虚函数)表现出不同的行为(如 Date 和 DateTime 都有Print接口,但打印格式不同)。

8.3 面向对象的优势

  • 可维护性:封装使代码模块化,修改一个类的内部逻辑不影响其他类。
  • 可扩展性:通过继承和多态,可轻松添加新功能(如增加新的日期格式,只需派生一个新类,无需修改原有代码)。
  • 可复用性:类可以被多个模块复用(如 Date 类可用于日历、闹钟等模块)。

九、补充疏漏点总结

  1. 成员声明与定义的区别

    • 声明:类内声明成员(如int _year;),仅告诉编译器 “成员存在”,不分配内存。
    • 定义:对象创建时(通过初始化列表),成员才分配内存并初始化(如Date d(2025);时,_year分配内存并赋值 2025)。
  2. 对象的内存布局

    • 非静态成员变量:存储在对象内存中,sizeof(对象) = 所有非静态成员变量的大小(需考虑内存对齐)。
    • 静态成员变量 / 函数:存储在静态区,不占用对象内存。
    • 成员函数:存储在代码区,所有对象共享同一套成员函数(通过this指针区分不同对象)。
  3. 初始化列表与类内缺省值的优先级

    • 初始化列表显式初始化 → 类内缺省值 → 默认初始化(内置类型随机值,自定义类型调用默认构造)。
  4. 友元的声明位置

    • 友元函数 / 类的声明可放在类内的任何区域(public/private/protected),不影响其友元权限。
http://www.dtcms.com/a/482941.html

相关文章:

  • 重庆建站服务商漳浦网站开发
  • 深入浅出理解电感:从理论到实践的电路“惯性”元件
  • 分布式事务:基于MQ事务的解决方案详解
  • 无信息先验:贝叶斯分析中的客观基准
  • 公司官网备案流程mysql优化 wordpress
  • 网站建设员课程注册网页版
  • 瑞莎星瑞(Radxa Orion O6) 基于 Android OS 使用 NPU的图片模糊查找APP 开发
  • 户外商品网站制作长沙网站建设的公司
  • 安卓13_ROM修改定制化-----ROM解打包 修改 讲解 导读篇
  • 网站设计亮点望野亭
  • RTC时钟原理
  • STM32运行原理深度解析:从软件到硬件的神奇之旅
  • OpenCV(十一):色彩空间转换
  • 广州安全教育平台网宁波网站seo哪家好
  • 家装网站自己做的平面设计常用网站
  • Three.js轨道控制器完全指南(OrbitControls与TrackballControls)
  • 服务器数据恢复—硬盘黄灯预警,RAID5阵列数据如何恢复?
  • CATIA 转换为 3DXML 全流程:迪威模型网在线转换和本地方转换方法指南
  • 学校门户网站建设的意义做任务分享赚钱的网站
  • 网站个人中心wordpress怎么做手机网站
  • 杂记 15
  • Video Understanding Baseline via papers
  • MySQL架构和存储引擎
  • Zabbix模板,自定义键值监控项,图形
  • 前端js 常见算法面试题目详解
  • 盾思途旅游网站建设免费seo工具
  • 吴江区经济开发区建设工程网站网站对于企业的好处
  • 新的pvc是否可以指定pv, 而这个pv已经被另一个pvc绑定,状态为bound
  • 网站域名在哪里买巩义网站建设案例
  • 微软宣布删除“另存为”选项,今后文件将默认保存到云盘