【C++初阶】第六篇——类和对象(下)(初始化列表+explicit关键字+static成员+友元+内部类)

⭐️今天我要和大家分享C++中类和对象中的最后一篇,来做一个补充。
⭐️博客代码已上传至gitee:https://gitee.com/byte-binxin/cpp-class-code


🌏构造函数中的初始化列表

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

class
{
public:
	Date(int year, int month, int day)// 成员变量定义处  给变量开空间
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	// 成员变量声明处  
	int _year;
	int _month;
	int _day;
};

注意:

  1. 每个成员变量只能在初始化列表中出现一次
  2. 类中包含定义时必须初始化的成员时,必须使用初始化列表
  • const成员变量(常量)
  • 引用成员变量(引用必须在定义时初始化)
  • 无默认构造函数(无参、全缺省和编译器给的默认构造函数)的自定义类型成员变量
class AA
{
public:
    AA(int a)// AA类无默认构造函数,必须使用初始化列表
    {
        _a = a;
    }
private:
    int _a;
};
class Date
{
    Date(int year = 1, int month = 1, int day = 1, int i = 10)
        :_a(10)
        ,_ref(i)
        ,_aa(10)
    {
        _year = year;
        _month = month;
        _day = day;
    }


    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    
private:
    // 成员变量声明的地方 还未申请空间
    int _year;
    int _month;
    int _day;
	
	// 以下三个成员变量都是必须在定义时初始化
    const int _a;
    int& _ref;
    AA _aa;
};

我们一般尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。

一个小问题

看下面这串代码运行结果是什么?

class A
{
public:
  A(int a)
       :_a2(a)
       ,_a1(_a2)
   {

   }
   void Print()
   {
      cout << _a1 << " " << _a2 << endl;
   }
private:
   int _a1;
   int _a2;
};

int main()
{
   A a(10);
   a.Print();

   return 0;
}

代码运行结果如下:
在这里插入图片描述
解释:
成员变量在初始化列表的初始化顺序与声明顺序有关,和在初始化列表的先后顺序无关。
代码中是 _a1 声明顺序先于 _a2 ,所以 _a2 先给 _a1 赋值,因为此时 _a2 里面放的是随机值,所以 _a1 是随机值,然后10赋值给 _a2 ,所以 _a2 就是10.

🌏explicit关键字

在C语言中,有一个隐式类型转换的概念,相近类型,意义相似的类型可以发生隐式类型转换。例如int和double两个类型都是表示大小的,所以可以发生。那么,在自定义类型中是否可以发生这中转换吗?
先看下面一串代码:

class Date
{
public:
    Date(int year, int month, int day)
        :_year(year)
        ,_month(month)
        ,_day(day)
    {}
    
private:
    int _year;
    int _month :
    int _day;
}int main()
{
    Date d1 = { 2022, 1, 21 };

    return 0;
}

其中,Date d1 = { 2022, 1, 21 }; 这里其实是编译器先用{ 2022, 1, 21 }构造一个匿名对象,然后把这个对象给d1赋值,因为两次构造是连续的,所以两个动作会被编译器优化为一个构造,两次都是直接构造,但是意义还是不同的。
如果我们不想这种隐式类型转换发生,我们一般在构造函数前面加一个explicit的关键字。

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

🌏static成员

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

特性:

  • 静态成员属于整个类,且不占类的大小
  • 静态成员变量是在类内声明,类外定义的
  • 静态成员可以直接通过::指定类域来访问
  • 静态成员函数中没有this指针,不能访问非静态的成员

我们实现一个类,计算该程序创建了多少个对象

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
    {
        _sCount++;
        _year = year;
        _month = month;
        _day = day;
    }

    // 静态成员函数  无this指针,可以直接用::指定类域访问
    // 只能访问静态成员变量和函数
    static int GetCount()
    {
        return _sCount;
    }

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    
private:
    // 成员变量声明的地方
    int _year;
    int _month;
    int _day;

    // 静态成员变量属于整个类(所有类)生命周期在整个程序运行期间
    // 可以直接用::指定类域访问
    static int _sCount;// 类内声明,类外初始化定义
};

// 静态必须要在类外初始化定义
int Date::_sCount = 0;

🌏C++11中新的初始化方式

class Date
{
public:

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    
private:
    // 成员变量声明的地方
    // 注意,这里不是定义,而是声明,在声明是给缺省值
    int _year = 1;
    int _month = 1;
    int _day = 1;
    static int _sCount;// 静态成员变量不能像上面一样
};

// 静态必须要在类外初始化定义
int Date::_sCount = 0;

🌏友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

💎友元函数

我们实现一个在类外实现operator<< 和 **operator>>**重载运算符函数。如果放在类内的话,那么this指针就是第一个默认的参数,所以我们选择在类外实现,可以调换参数的顺序。


class Date
{
    // 友元函数
    // 不能用const修饰  const修饰的是非静态的成员函数 一个类可以是很多函数的友元
    friend ostream& operator<<(ostream& out, const Date& d);
    friend istream& operator>>(istream& in, Date& d);
public:
    Date(int year = 1, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    
private:
    int _year;
    int _month;
    int _day;
};



// 全局
ostream& operator<<(ostream& out, const Date& d)
{
    out << d._year << "-" << d._month << "-" << d._day << endl;

    return out;
}

istream& operator>>(istream& in, Date& d)
{
    in >> d._year >> d._month >> d._day;

    return in;
}

特性:

  • 不能用const修饰
  • const修饰的是非静态的成员函数
  • 一个类可以是很多函数的友元

💎友元类

友元类,就是让一个类称为另一个类的友元,这样这个类就可以访问另一个类中的所有私有属性。

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    
private:
    int _year;
    int _month;
    int _day;
};

class Time
{
    // 友元类
    // Date类是Time类的友元   Date可以访问Time的私有成员  单向  不能传递
    friend class Date;
public:
    Time(int hour, int minute, int second)
        :_hour(hour)
        , _minute(minute)
        , _second(second)
    {}
private:
    int _hour;
    int _minute;
    int _second;
};

🌏内部类

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

特性:

  • 外部类天生就是内部类的友元类,内部类具有访问外部类私有属性的权限,但外部类却没有访问内部类的私有属性的权限
  • 内部类只是受外部类的类域的限制,内部类并不属于外部类,也就是sizeof(外部类)=外部类
class A
{
private:
    static int _a;
    int _b;
public:
    // 内部类
    // B天生就是A的友元   B可以访问A的私有成员,但是A不可以访问B的私有
    // sizeof(A) = 4    A中成员变量b  a属于整个类,不占对象的大小  内部类B和在全局定义的基本一致,知识受A类域的限制
    class B
    {
    public:
        void f()
        {

        }
    private:
        int _a;
    };
};

int A::_a = 10;

🌐总结

C++的类和对象全部内容就介绍到这里了,喜欢的话,欢迎点赞支持~
在这里插入图片描述

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆兽学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值