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

《C++初阶之类和对象》【初始化列表 + 自定义类型转换 + static成员】

【初始化列表 + 自定义类型转换 + static成员】目录

  • 前言:
  • ---------------初始化列表---------------
    • 什么是初始化列表?
    • 为什么要使用初始化列表?
    • 怎么使用初始化列表?
    • 初始化列表 vs 构造函数体内赋值谁能获胜?
    • 使用初始化列表的注意事项有哪些?
      • 勇敢的少年,快去创造奇迹吧?
    • C++11中的新特性缺省成员初始化怎么使用?
    • 成员变量走初始化列表的时机是什么?
  • ---------------用户自定义类型转换---------------
    • 用户自定义类型转换有哪些?
    • 什么是explicit关键字?
    • 为什么需要explicit关键字?
    • 怎么使用explicit关键字?
  • ---------------static成员---------------
    • 什么是static成员?
    • 什么是static成员变量?
    • static成员变量的特点有哪些?(大总结)
    • 什么是static成员函数?
    • static成员函数的特点有哪些?(大总结)
    • 静态成员怎么使用?

在这里插入图片描述

往期《C++初阶》回顾:

/------------ 入门基础 ------------/
【C++的前世今生】
【命名空间 + 输入&输出 + 缺省参数 + 函数重载】
【普通引用 + 常量引用 + 内联函数 + nullptr】
/------------ 类和对象 ------------/
【类 + 类域 + 访问限定符 + 对象的大小 + this指针】
【类的六大默认成员函数】

前言:

哈喽呀小伙伴们~一周不见想死你们啦!(づ。◕‿‿◕。)づ
上周有没有乖乖学习呀?🤔
至于博主我嘛👉 刚忙完期末复习大作战💻 ,博主可是被期末复习按在地上摩擦了一周呢 (╯°□°)╯︵ ┻━┻

不过现在终于忙完啦!虽然还有一周才考试…但是!咱这闲不住的人哪能真闲着呀✨
所以!今天就马不停蹄给大家带来《C++ 初阶之类和对象》【下】的第一篇宝藏博客啦🎁(๑•̀ㅂ•́)و✧
据说看完这篇的小伙伴都直呼"原来C++还能这样玩?"(≧∇≦)ノ 快搬好小板凳准备上课啦~

---------------初始化列表---------------

什么是初始化列表?

初始化列表(Initializer List):是 C++ 中用于在对象构造时直接初始化成员变量的语法。

  • 它位于构造函数的参数列表之后,函数体之前,以冒号:开始,后面跟着用逗号分隔的成员变量初始化列表。

  • 它核心作用是在对象内存分配完成后立即初始化成员,而非先默认初始化再赋值。

class MyClass 
{int _a;double _b;std::string _s;
public:// 初始化列表语法MyClass(int a, double b, const std::string& s) : _a(a), _b(b), _s(s)  // 初始化列表{// 构造函数体(此时成员已初始化)}
};

为什么要使用初始化列表?

使用初始化列表主要有以下两点原因:

  1. 为了提高类的成员变量的初始化效率
  2. 有些成员变量不得不使用初始化列表进行初始化

--------------------为了提高效率--------------------

对于类类型的成员变量 :

  • 如果没有使用初始化列表,在进入构造函数体之前,会先调用成员变量的默认构造函数进行初始化,然后在构造函数体中再进行赋值操作,这会产生额外的开销。
  • 而使用初始化列表,可以直接调用成员变量的合适构造函数进行初始化,避免了先默认构造再赋值的过程,从而提高了初始化效率。
    • 例如:当成员变量是string类型时,使用初始化列表可以直接调用string的构造函数来初始化,而不是先默认构造一个空字符串,再进行赋值。
class Student 
{string _name;
public:// 低效写法:先默认构造空字符串,再赋值Student(const std::string& name) { _name = name; }// 高效写法:直接调用string的拷贝构造Student(const string& name) : _name(name) {}
};

--------------------不得不使用--------------------

初始化 const 成员和引用成员

  • const成员变量和引用成员在定义后就不能被修改,因此必须在定义时进行初始化。

  • 初始化列表是在构造函数中初始化const成员和引用成员的唯一方法。如果不使用初始化列表,试图在构造函数体中对const成员或引用成员进行赋值,将会导致编译错误。

----------------------------初始化 const 成员----------------------------
class ConstDemo 
{const int _value;
public:ConstDemo(int v) : _value(v) {} // 必须用初始化列表
};----------------------------初始化 const 成员----------------------------class RefDemo 
{int& _ref;
public:RefDemo(int& r) : _ref(r) {} // 必须用初始化列表
};   

调用无默认构造的成员

  • 当成员变量的类没有默认构造函数,只有带参数的构造函数时,必须使用初始化列表来传递参数进行初始化。

  • 否则,编译器无法找到合适的构造函数来初始化成员变量,从而引发编译错误。

class Engine 
{
public:Engine(int power) {} // 只有带参构造
};
class Car 
{Engine _engine;
public:Car() : _engine(100) {} // 必须显式初始化
};

怎么使用初始化列表?

#include<iostream>
using namespace std;// Time类定义
class Time
{
public:/*---------------Time类的构造函数(带参数)---------------*/// 注意:这个类没有提供默认构造函数(无参构造函数)Time(int hour):_hour(hour)  {cout << "Time类的构造函数Time()" << endl; }
private:int _hour; 
};// Date类定义
class Date
{
public:/*---------------Date类的构造函数---------------*/Date(int& x, int year = 1, int month = 1, int day = 1):_year(year)      // 初始化_year, _month(month)   // 初始化_month, _day(day)       // 初始化_day, _t(12)          // 初始化Time类成员(必须提供参数), _ref(x)         // 初始化引用成员(必须通过初始化列表), _n(1)           // 初始化const成员(必须通过初始化列表){// 如果尝试在构造函数体内初始化以下成员,会导致编译错误:// 1. _t(12) - 错误:Time类没有默认构造函数// 2. _ref = x - 错误:引用必须在初始化列表中初始化// 3. _n = 1 - 错误:const成员必须在初始化列表中初始化}/*---------------打印日期信息---------------*/void Print() const{cout << _year << "-" << _month << "-" << _day << endl; //const成员函数,保证不会修改对象状态}private:int _year;    int _month;   int _day;     Time _t;      // Time类成员(没有默认构造函数)int& _ref;    // 引用成员  (必须在初始化列表中初始化)const int _n; // const成员 (必须在初始化列表中初始化)
};int main()
{int i = 0;  //定义一个整型变量用于初始化Date的引用成员// 创建Date对象d1// 参数://   i - 用于初始化_ref引用成员//   其他参数使用默认值Date d1(i);// 调用Print方法输出日期信息d1.Print();return 0;
}/** 关键点总结:* 1. 初始化列表的必要性:*    - 必须用于初始化:引用成员(_ref)、const成员(_n)、没有默认构造函数的类成员(_t)** 2. 初始化顺序:*    - 成员变量的初始化顺序由它们在类中的声明顺序决定(_year→_month→_day→_t→_ref→_n)*    - 与初始化列表中的书写顺序无关** 3. 特殊成员初始化:*    - 引用和const成员:只能在初始化列表中初始化*    - 没有默认构造的类成员:必须在初始化列表中显式构造** 4. 良好实践:*    - 即使对普通成员变量(如:_year)也使用初始化列表*    - 保持初始化列表顺序与成员声明顺序一致*/

在这里插入图片描述

初始化列表 vs 构造函数体内赋值谁能获胜?

特性初始化列表构造函数内赋值
const成员✔️ 支持❌ 编译错误
引用成员✔️ 支持❌ 编译错误
无默认构造的成员✔️ 支持❌ 编译错误
性能更高效
(直接构造)
可能低效
(先默认构造再赋值)
初始化顺序由成员声明顺序决定无顺序依赖

使用初始化列表的注意事项有哪些?

在 C++ 中,成员变量的初始化遵循两大规则:


其一:初始化顺序仅取决于变量在类中声明的先后次序,与初始化列表中的排列顺序无关。

class Order 
{int a; // 先声明int b;
public:Order() : b(1), a(2) {} // 实际初始化顺序:a→b
};

其二:每个成员变量在初始化列表中只能出现一次,作为其完成定义与初始化的关键位置,确保对象创建时各成员获得唯一且正确的初始状态。

勇敢的少年,快去创造奇迹吧?

下面是一道关于:初始化顺序的自测题(看完代码不要看下面的注释分析,因为那是答案)😄

#include <iostream>
using namespace std;class A
{
public:/*---------------构造函数---------------*//*** 初始化列表说明:* 1. _a1初始化为参数a的值* 2. _a2初始化为_a1的值(此时_a1尚未初始化,存在风险)** 注意:成员初始化顺序由声明顺序决定,与初始化列表顺序无关*/A(int a):_a1(a)      // 初始化_a1为传入的参数a, _a2(_a1)   // 用_a1的值初始化_a2(此时_a1尚未初始化){}/*---------------打印成员变量值---------------*/void Print() {cout << _a1 << " " << _a2 << endl;}private:// 注意成员变量的声明顺序:_a2在前,_a1在后// 这将影响初始化顺序,即使初始化列表中的顺序不同int _a2 = 2;  // 成员_a2,有类内初始值2(但会被初始化列表覆盖)int _a1 = 2;  // 成员_a1,有类内初始值2(但会被初始化列表覆盖)
};int main()
{// 创建A类对象,传入参数1A aa(1);// 打印对象成员值aa.Print();return 0;
}-------------------------------我下面是:分析和答案----------------------------/** 关键点分析:* 1. 成员初始化顺序:*    - 严格按照类中声明的顺序进行(先_a2,后_a1)*    - 与初始化列表中的书写顺序无关** 2. 初始化过程:*    a) 首先尝试初始化_a2:*       - 使用_a1的值(此时_a1尚未初始化,值不确定)*       - 类内初始值2被初始化列表覆盖*    b) 然后初始化_a1:*       - 使用构造函数参数1*    c) 最后执行构造函数体(本例为空)** 4. 类内初始值的作用:*    - 如果初始化列表不显式初始化成员,则使用类内初始值*    - 本例中初始化列表覆盖了类内初始值*/

在这里插入图片描述

C++11中的新特性缺省成员初始化怎么使用?

类成员变量可以在声明时直接指定初始值(缺省值),缺省值会在构造函数调用前生效,这些值会在对象构造时被使用,除非初始化列表中显式覆盖它们。

#include<iostream>
using namespace std;// Time类定义
class Time
{
public:/*---------------Time类构造函数---------------*/Time(int hour):_hour(hour)  {cout << "Time类构造函数Time(int hour)调用" << endl;  }
private:int _hour; 
};// Date类定义
class Date
{
public:/*---------------Date类默认构造函数---------------*/Date():_month(2)  // 显式初始化_month为2(会覆盖缺省值1){// 构造函数体内可以执行其他操作cout << "Date类默认构造函数Date()调用" << endl;  /** 注意:以下成员已在类定义中提供了缺省值:* _year = 1* _day 未显式初始化(内置类型未初始化时为随机值)* _t = 1 (调用Time(int)构造函数)* _n = 1 (const成员)* _ptr = (int*)malloc(12) (动态分配内存)*/}/*---------------打印日期信息---------------*/void Print() const{// 注意:_day未初始化,打印的值是未定义的cout << _year << "-" << _month << "-" << _day << endl;}private:// 成员变量声明与缺省值初始化(C++11特性)// 内置类型缺省值(如果初始化列表不显式初始化则使用此值)int _year = 1;   // 年份,缺省值为1int _month = 1;  // 月份,缺省值为1(但构造函数初始化列表会覆盖为2)int _day;        // 日期,没有缺省值(未初始化)// 类类型成员缺省初始化Time _t = 1;     // 调用Time(int)构造函数初始化// const成员缺省值const int _n = 1; // const成员必须在声明时或初始化列表中初始化// 指针成员动态内存分配int* _ptr = (int*)malloc(12); // 使用malloc分配12字节内存
};int main()
{// 创建Date对象d1Date d1;      //调用默认构造函数// 打印日期信息d1.Print();  //输出格式:year-month-dayreturn 0;
}/** 关键点总结:* 1. 缺省成员初始化(C++11特性):*    - 在类声明中直接为成员变量指定初始值*    - 如果构造函数初始化列表没有显式初始化,则使用缺省值(初始化列表的优先级高于缺省值)*    - 初始化列表的初始化会覆盖缺省值(如:_month被初始化为2而非1)** 2. 成员初始化顺序:*    - 首先按照缺省值初始化*    - 然后执行构造函数初始化列表的初始化*    - 最后执行构造函数体内的代码** 3. 特殊成员处理:*    - _day:没有缺省值,未在初始化列表中初始化,值是未定义的*    - _t:类类型成员,使用Time(int)构造函数初始化*    - _n:const成员,必须在声明时或初始化列表中初始化*    - _ptr:使用malloc动态分配内存*/

在这里插入图片描述

成员变量走初始化列表的时机是什么?

初始化列表的特点:

  • 先于构造函数体:无论成员变量是否在初始化列表中显式列出,其初始化都在构造函数体执行 之前 完成
  • 隐式初始化:未在初始化列表中显式初始化的成员变量,仍会按默认规则初始化(如:默认构造函数、缺省值或未定义值)

在 C++ 中,推荐优先使用初始化列表对成员变量进行初始化,

原因如下:即使成员变量未在初始化列表中显式初始化,其初始化过程依然会经过初始化列表阶段。


若成员变量在声明时指定了默认值,初始化列表将直接采用该默认值进行初始化。

未指定默认值

  • 对于内置类型成员,其是否初始化由编译器决定,C++ 标准未作强制规定,这可能导致变量处于未初始化的不确定状态。
  • 对于自定义类型成员
    • 不存在默认构造函数:若该自定义类型不存在默认构造函数,编译器无法自动完成初始化,就会引发编译错误。
    • 存在默认构造函数
      • 若自定义类型成员未在初始化列表中显式初始化,编译器将调用该类型的 默认构造函数 进行初始化。
      • 若自定义类型成员在初始化列表中显式初始化,编译器将直接调用该类型对应的 构造函数,而非默认构造函数。
#include <iostream>
using namespace std;class MyClass
{
public:MyClass(){cout << "默认构造函数被调用" << endl;}MyClass(int value){cout << "带参构造函数被调用,参数:" << value << endl;}
};// 案例1:未显式初始化(调用默认构造函数)
class Container1
{
private:MyClass obj1;  
public:Container1() //未在初始化列表中显式初始化{cout << "Container1构造函数体执行" << endl;}
};// 案例2:显式初始化(调用带参构造函数)
class Container2
{
private:MyClass obj2;
public:Container2() : obj2(42) //显式初始化{cout << "Container2构造函数体执行" << endl;}
};int main() 
{cout << "==== 案例1 ====" << endl;Container1 c1;cout << "\n==== 案例2 ====" << endl;Container2 c2;return 0;
}

在这里插入图片描述

---------------用户自定义类型转换---------------

用户自定义类型转换有哪些?

自定义类型转换的种类:

  • 内置类型 → 类类型
    当类定义了单参数构造函数(或:除第一个参数外其余参数均有默认值)时,C++ 允许将该参数类型的值隐式转换为类对象。
    示例

    class MyClass 
    {
    public:MyClass(int value) { /* ... */ }  // 单参数构造函数
    };void func(MyClass obj);func(42);  // 隐式转换:int → MyClass
    
  • 类类型 → 类类型
    若类 A 定义了以类 B 为参数的构造函数,则类 B 的对象可隐式转换为类 A 的对象。
    示例

    class B {};
    class A 
    {
    public:A(const B& b) { /* ... */ }  // 以B为参数的构造函数
    };void func(A obj);
    B b;
    func(b);  // 隐式转换:B → A
    

注意:上面的这两种类型转换都是隐式类型转换

代码小案例:展示使用自定义类型的类型转换

#include<iostream>
using namespace std;class A
{
public:/*----------------------单参数构造函数----------------------*//*** 注意:如果加上explicit关键字,将禁止隐式类型转换* 例如:A aa1 = 1; 这样的语句将无法编译*/A(int a1):_a1(a1){}/*----------------------双参数构造函数----------------------*//*** 注意:如果加上explicit关键字,将禁止隐式类型转换* 例如:A aa3 = {2,2}; 这样的语句将无法编译*/A(int a1, int a2):_a1(a1), _a2(a2){}/*----------------------打印成员变量值----------------------*/void Print(){cout << _a1 << " " << _a2 << endl;}int Get() const{return _a1 + _a2;}private:int _a1 = 1;  // 成员变量_a1,默认值为1int _a2 = 2;  // 成员变量_a2,默认值为2
};class B
{
public:/*----------------------构造函数,接收A类型的引用----------------------*//*** 这个构造函数允许从A类型到B类型的隐式转换*/B(const A& a):_b(a.Get())  // 使用A对象的Get()方法初始化_b{}private:int _b = 0;  // 成员变量_b,默认值为0
};int main()
{// 1. 隐式类型转换示例1:int → A// 编译器实际执行:// 1) 先用1构造一个临时A对象// 2) 再用这个临时对象拷贝构造aa1// 3) 编译器优化为直接构造A aa1 = 1;  // 等价于 A aa1(1);aa1.Print();  // 输出:1 2// 2. 隐式类型转换示例2:int → A → const A&// 用1构造临时A对象,然后绑定到引用aa2const A& aa2 = 1;  // 临时对象生命周期延长至引用作用域结束// 3. C++11列表初始化(多参数隐式转换)// 使用初始化列表构造A对象A aa3 = { 2,2 };  // 等价于 A aa3(2,2);aa3.Print();  // 输出:2 2// 4. 隐式类型转换示例3:A → B// 通过A对象隐式构造B对象// 实际过程:// 1) 调用A::Get()获取值// 2) 用该值构造B对象B b = aa3;  // 等价于 B b(aa3);// 5. 隐式类型转换示例4:A → B → const B&// 通过A对象隐式构造临时B对象,然后绑定到引用const B& rb = aa3;return 0;
}/** 关键点总结:* 1. 隐式类型转换规则:*    - 当类有单参数构造函数时,支持从参数类型到类类型的隐式转换*    - C++11开始支持多参数构造函数的隐式转换(使用初始化列表语法)** 2. explicit关键字的作用:*    - 修饰构造函数时,禁止隐式类型转换*    - 需要显式调用构造函数(如 A aa1(1) 而不是 A aa1 = 1)** 3. 引用绑定临时对象:*    - const引用可以绑定到隐式转换产生的临时对象*    - 临时对象的生命周期会延长至引用的作用域结束*/

在这里插入图片描述

什么是explicit关键字?

explicit 关键字:是 C++ 中用于修饰 构造函数类型转换运算符关键字

  • 它的核心作用是 禁止隐式类型转换,强制要求程序员进行显式转换,从而提高代码的安全性和可读性。

示例

class MyClass 
{
public:explicit MyClass(int value) { /* ... */ }  // 禁止隐式转换
};void func(MyClass obj);func(42);           // 错误:无法隐式转换
func(MyClass(42));  // 正确:显式构造

为什么需要explicit关键字?

在没有 explicit 的情况下,C++ 允许 隐式转换,这可能导致意外的行为。

class MyString 
{
public:MyString(const char* str) { /* 构造函数 */ }void print() { cout << str; }
private:string str;
};void func(MyString s) 
{ s.print(); 
}int main() 
{func("Hello");  // 隐式转换:const char* → MyStringreturn 0;
}

问题

  • func("Hello") 会自动调用 MyString(const char*) 构造函数,但可能并非程序员本意。
  • 如果 MyString 有多个构造函数,隐式转换可能导致歧义。

怎么使用explicit关键字?

修饰构造函数:禁止编译器进行隐式构造,必须显式调用。

class MyString 
{
public:explicit MyString(const char* str) { /* ... */ }  // 禁止隐式转换
};void func(MyString s) 
{ s.print(); 
}int main() 
{// func("Hello");  // ❌ 错误:不能隐式转换func(MyString("Hello"));  // ✅ 必须显式构造return 0;
}

修饰类型转换运算符:禁止隐式类型转换,必须显式转换。

class MyInt 
{
public:explicit operator int() const  //禁止隐式转换{  return value;}
private:int value;
};int main() 
{MyInt num;// int x = num;  // ❌ 错误:不能隐式转换int x = static_cast<int>(num);  // ✅ 必须显式转换return 0;
}

---------------static成员---------------

什么是static成员?

static成员:是类的特殊成员,它们 不属于任何一个类对象,而是 属于类本身,被所有类对象共享。


static 成员分为两种:

  1. 静态成员变量(Static Data Members)
  2. 静态成员函数(Static Member Functions)

什么是static成员变量?

static成员变量:用static修饰的成员变量。

  • 定义与特性

    • 它属于整个类,而非类的某个具体对象。
    • 无论类创建了多少个对象,static成员变量只有一份实例,被所有对象共享。
  • 内存分配与初始化

    • 它在程序启动时分配内存。

    • 它必须在类外进行初始化。(即使有默认值也需在类外显式初始化)

      class MyClass 
      {
      public:static int sharedVar;    //声明静态成员变量
      };
      int MyClass::sharedVar = 10; //在类外初始化
      
  • 访问方式

    • 可以通过类名加作用域解析符::直接访问。
      • 例如MyClass obj; int value1 = MyClass::sharedVar;
    • 也可以能通过对象来访问。
      • 例如int value2 = obj.sharedVar; ,但推荐用类名访问,更能体现其静态属性。
  • 作用:常用于统计类对象的数量,或者存储类的全局相关信息。

static成员变量的特点有哪些?(大总结)

1. 存储特性

  • 全局唯一性:无论创建多少个类的对象,static成员变量在内存中仅有一份实例,被所有对象共享。
  • 静态存储区:存储在程序的静态存储区,生命周期从程序启动到结束,不依赖于对象的创建与销毁。

2. 初始化规则

  • 类外显式初始化:必须在类外进行初始化。
  • 不支持类内默认值:C++ 标准禁止在类内直接为static成员变量设置缺省值。(除非是constexpr类型)

3. 访问特性

  • 类名直接访问:可通过类名::静态成员名访问,无需创建对象。
  • 受访问限定符约束:与普通成员变量相同,受public/protected/private限制。

4. 功能特性

  • 类级别数据:用于存储与类相关的全局信息(如:对象计数、配置参数)
  • 独立于对象:不与任何对象绑定,可在无对象实例时使用。
  • 可被const成员函数修改static成员变量可在const成员函数中被修改,因其不属于任何对象实例。

什么是static成员函数?

static 成员函数:用static修饰的成员函数。

  • 定义与特性

    • 同样属于类本身,它不依赖于类的具体对象,没有隐含的this指针(不能访问非静态成员变量非静态成员函数

      class MyClass 
      {
      public:static void staticFunc() {// 这里只能访问静态成员变量,不能访问非静态成员// 如不能访问非静态成员变量:nonStaticVar}static int sharedVar;
      private:int nonStaticVar;
      };
      int MyClass::sharedVar = 0;
      
  • 调用方式

    • 可以通过类名直接调用。
      • 例如MyClass::staticFunc();
    • 也可以通过对象调用,但不推荐。
  • 用途:通常用于实现一些与类整体相关,而不涉及具体对象状态的功能。

    • 例如:为类提供工具性的方法,或操作类的静态成员变量。

static成员函数的特点有哪些?(大总结)

1. 归属特性

  • 属于类而非对象static成员函数不绑定到任何对象实例,即使没有创建类的对象也可调用。
  • 无this指针:无法使用this指针,因此不能访问非静态成员变量调用非静态成员函数

2. 调用方式

  • 类名直接调用:通过类名::静态函数名()调用,无需创建对象。
  • 对象调用允许但不推荐:也可通过对象实例调用(如:obj.staticFunc()),但语义上不直观。

3. 访问权限

  • 仅访问静态成员:只能访问类的静态成员变量和静态成员函数,无法访问非静态成员(包括普通成员变量和非静态成员函数)。
  • 受访问限定符约束:与普通成员函数相同,受public/protected/private限制。

4. 功能特性

  • 工具性方法:常用于实现与类相关但不依赖对象状态的工具函数(如:工厂方法、配置管理)
  • 全局接口封装:可作为类的全局接口,隐藏类的实现细节(如:单例模式中的getInstance()方法)

5. 特殊规则

  • 不能声明为const:由于没有this指针,static成员函数不能被声明为const
  • 不能是虚函数static成员函数不参与多态,无法被声明为virtual
  • 可在类内定义:可直接在类内实现,也可在类外实现(需指定类名::作用域)

静态成员怎么使用?

代码案例:使用静态成员变量和静态成员函数

#include<iostream>
using namespace std;class A
{
public:/*----------------------默认构造函数----------------------*/A(){++_scount;  // 每创建一个对象时,静态计数器_scount加1}/*----------------------拷贝构造函数----------------------*/A(const A& t){++_scount;  // 每拷贝构造一个对象时,静态计数器_scount加1}/*----------------------析构函数----------------------*/~A(){--_scount;  // 对象销毁时,静态计数器_scount减1}/*----------------------静态成员函数----------------------*//*** @brief 静态成员函数:获取当前对象数量* @return 当前存在的对象数量** 注意:* 1. 静态成员函数可以直接通过类名调用* 2. 静态成员函数只能访问静态成员变量*/static int GetACount(){return _scount;}private:// 静态成员变量声明(类内)static int _scount; //用于记录当前存在的A类对象数量
};// 静态成员变量定义和初始化(类外)
// 必须单独在类外进行定义和初始化
int A::_scount = 0;  int main()
{// 测试1:初始对象数量cout << A::GetACount() << endl;  // 输出:0(尚未创建任何对象)// 测试2:创建对象后的数量A a1, a2;       // 调用两次默认构造函数(_scount = 2)A a3(a1);       // 调用拷贝构造函数(_scount = 3)cout << A::GetACount() << endl;  // 输出:3(三个对象存在)// 测试3:通过对象调用静态成员函数// 语法允许但不推荐,容易造成误解cout << a1.GetACount() << endl;  // 输出:3(与类名调用结果相同)// 测试4:尝试直接访问私有静态成员(编译错误)// 错误原因:_scount是private成员,只能在类内部访问//cout << A::_scount << endl;  return 0;// main函数结束时,a1,a2,a3依次析构,_scount变为0
}/** 关键点总结:* 1. 静态成员变量_scount:*    - 在类内声明,类外初始化*    - 所有对象共享同一个_scount*    - 用于记录程序中A类对象的实时数量** 2. 静态成员函数GetACount():*    - 可以直接通过类名调用(推荐方式)*    - 也可以通过对象调用(语法允许但不推荐)*    - 只能访问静态成员变量(无法访问普通成员变量)** 3. 访问控制:*    - _scount是private成员,外部无法直接访问*    - 必须通过public的GetACount()方法获取*/

在这里插入图片描述
在这里插入图片描述

http://www.lryc.cn/news/574544.html

相关文章:

  • 在 centos7部署kubephere
  • TortoiseSVN 安装教程
  • prometheus+grafana+MySQL监控
  • 云原生周刊:Argo CD v3.1 正式发布
  • 工程优化——WebSocket、WSS(WebSocket Secure)和SSE(Server-Sent Events)通信对比
  • Jenkins+Jmeter+Ant接口持续集成
  • 【人工智能agent】--dify实现文档内容的自动抽取
  • 论文阅读:2025 arxiv Qwen3 Technical Report
  • 【论文阅读 | CVPRW 2023 |CSSA :基于通道切换和空间注意力的多模态目标检测】
  • 【AI时代速通QT】第三节:Linux环境中安装QT并做测试调试
  • Starrocks 低基数全局字典优化
  • 【Vue】 keep-alive缓存组件实战指南
  • Dify携手代理商奇墨科技:加快企业AI应用构建
  • FTP原理、安装部署与案例应用全面指南
  • Unity3D下的RTSP/RTMP超低延迟直播播放器实践:跨平台、高性能与VR全景支持的完整解析
  • 创建首个 Spring Boot 登录项目
  • DD3118S:USB3.0+Type-c双头TF/SD二合一高速0TG多功能手机读卡器ic
  • 76、单元测试-参数化测试
  • 做上门私厨/上门做饭App小程序,到底是定制开发,还是选成品系统?
  • 随机森林详解:原理、优势与应用实践
  • 【空间数据分析】全局莫兰指数(Global Moran’s I)
  • 《C++》命名空间简述
  • 项目练习:使用itextpdf制作pdf报表
  • 电商场景BI解决方案:用观远BI捕获电商大促增长先机
  • (3)ROS2:6-dof前馈+PD / 阻抗控制器
  • 常见网络知识,宽带、路由器
  • UAVAI-YOLO:无人机航拍图像的小目标检测模型
  • NLP基础1_word-embedding
  • 桥头守望者
  • iostat中的util原理