C++初始化列表的三大强制场景:告别低效对象构造

1、初始化列表

构造函数体赋值:在构造函数体内通过赋值操作设置成员变量的值,本质是先默认初始化再赋值
初始化列表:在对象创建时直接调用成员变量的构造函数,一步完成初始化。以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式

// 构造函数体赋值(低效)
Date(int year, int month, int day) {
    _year = year;   // 赋值操作
    _month = month;
    _day = day;
}

// 初始化列表(高效)
Date(int year, int month, int day) 
    : _year(year)
    , _month(month)
    , _day(day) 
{}

必须使用初始化列表的场景

  • const成员变量
  • 引用成员变量
  • 自定义成员类型(且该类没有默认构造函数时)
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 
};

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

比如:

class A
{
public:
    A(int a)// 初始化顺序由成员变量声明顺序决定
    :_a1(a)
    ,_a2(_a1)
    {}
 
    void Print() 
    {
        cout<<_a1<<" "<<_a2<<endl;
    }
private:
    int _a2;
    int _a1;
};
 
int main() 
{
    A aa(1);
    aa.Print();//输出:1  随机值
}

_a2在_a1之前声明,所以_a2会比_a1先初始化。


1.1、explicit关键字

构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。但是用explicit修饰构造函数,将会禁止构造函数的隐式转换。

比如:

class Date 
{
public:
    // explicit修饰构造函数,禁止了单参构造函数类型转换的作
    // 用
    explicit Date(int year) 
    : _year(year) 
    {}
    
};

int main()
{
    Date d1(2022);
    d1 = 2023;// 用一个整形变量给日期类型对象赋值
             // 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值
    return 0;
}

2、static成员

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

2.1 特性

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

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

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

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

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

那么,可以用static的特性来计算一个类里面创建出多少个对象:

class A
{
public:
   A() 
   { 
       ++_scount; 
   }

   A(const A& t) 
   {    
       ++_scount; 
   }

   ~A() 
   { 
       --_scount; 
   }

   static int GetACount() 
   { 
       return _scount; 
   }

private:
   static int _scount;
};
 
int A::_scount = 0;
 
void TestA()
{
    cout << A::GetACount() << endl;// 0
    A a1, a2;
    A a3(a1);
    cout << A::GetACount() << endl;// 3
}
  • 静态成员函数能否调用非静态成员?
    不能,因为缺少 this 指针。

  • 非静态成员函数能否调用静态成员?
    可以,静态成员属于类,与对象无关。


3、友元(友元函数和友元类)

友元是一种突破封装的方式。当两个类之间有非常紧密的关系,且需要频繁访问彼此的私有成员时,友元可以简化代码并提高效率。

3.1、友元函数

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

友元函数重载<<运算符:

class Date 
{
    friend ostream& operator<<(ostream& out, const Date& d);//声明友元
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day) 
        {}

private:
    int _year, _month, _day;
};

ostream& operator<<(ostream& out, const Date& d) 
{
    out << d._year << "-" << d._month << "-" << d._day;
    return out;
}
  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数

3.2、友元类

友元类的所有成员函数都能访问另一个类的私有成员。

class Time 
{
    friend class Date;  // Date是Time的友元类
public:
    Time(int h = 0, int m = 0, int s = 0) 
        : _hour(h)
        , _minute(m)
        , _second(s) 
        {}

private:
    int _hour, _minute, _second;
};

class Date   
{
public:
    void SetTime(int h, int m, int s) 
    {
        _t._hour = h;   // 直接访问Time的私有成员
        _t._minute = m;
        _t._second = s;
    }
private:
    Time _t;
};

注意事项

  • 友元关系是单向的(A是B的友元,B不一定是A的友元)

  • 慎用友元,避免破坏封装性


4、内部类

一个类定义在一个类的内部,就叫内部类。

  • 定义在类内部的独立类,与外部类无继承关系
  • 内部类天生是外部类的友元,可直接访问外部类的私有成员
  • 外部类不是内部类的友元

比如:

class Outer 
{
public:
    class Inner 
    {
    public:
        void Show(const Outer& o) 
        {
            cout << o._data; // 访问外部类私有成员
        }
    };

private:
    int _data = 42;
};

int main() 
{
    Outer::Inner inner;
    Outer outer;
    inner.Show(outer);  // 输出42
}

5、有名对象和匿名对象

有名对象 ---- 生命周期在当前函数局部域

匿名对象 ---- 生命周期在当前行

#include <iostream>
using namespace std;

class Date 
{
private:
    int _year;
    int _month;
    int _day;

public:
    Date(int year, int month, int day)
        : _year(year), _month(month), _day(day) 
        {}

};

int main() 
{
    Date(2023, 10, 11);  // 匿名对象

    const Date& today = Date(2023, 10, 11);  // 匿名对象绑定到 const 引用
    today.Print();  // 通过引用访问匿名对象

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值