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

Posted 呆呆兽学编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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++的类和对象全部内容就介绍到这里了,喜欢的话,欢迎点赞支持~

以上是关于C++初阶第六篇——类和对象(下)(初始化列表+explicit关键字+static成员+友元+内部类)的主要内容,如果未能解决你的问题,请参考以下文章

数据结构初阶第六篇——初识二叉树(二叉树的基本性质+二叉树的顺序存储结构及实现)

C++初阶第五篇——类和对象(中)(构造函数+析构函数+拷贝构造函数+赋值操作符重载)

C++初阶第一篇——初始C++(命名空间+缺省参数+函数重载)

C++初阶:类和对象(下篇)初始化列表 | static成员 | 友元

C++从青铜到王者第六篇:C++模板初阶

C++初阶第二篇——初始C++(详细讲解extern “C”)