• 类与对象(下)


    类与对象(下)

    1.const修饰对象

    const修饰成员函数

    我们知道在成员函数中如何保护参数的值不被修改,只需要在**参数的前面写上const**修饰即可。

    但是,如何保护this的成员变量不被修改呢,可能有同学说我在this指针前面加一个const不就可以了吗,可是this指针是一个隐藏的指针,你没法办像之前那样加const,c++规定了一个语法规则,这样加const即可保护this里面的成员变量不被修改

    代码演示:

    #include
    using namespace std;
    
    class Date
    {
    public:
    	Date(int year = 2003, int month = 6, int day = 10)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    
    	void Print() 
    	{
    		printf("%d-%d-%d\n", _year, _month, _day);
    	}
    
    	void Print() const
    	{
    		printf("%d-%d-%d\n", _year, _month, _day);
    	}
    
    	void f() const
    	{
    		//_year = 10;//error--const修饰成员函数无法改动成员变量
    	}
    
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1;
    	d1.Print();
    	const Date d2(2022, 9, 5);
        //这个就是说d2这个对象里面的成员变量是不允许通过成员函数被修改的
    	d2.Print();
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    const Date d2(2022, 9, 5);->这个就是说d2这个对象里面的成员变量是不允许通过成员函数被修改的

    语法规则:在成员函数后面加上const即可,上面两个Print成员函数也属于函数重载,d1对象调用的是非const的print成员函数,d2对象调用的是const的成员函数。

    const修饰成员函数无法改动成员变量


    图解演示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IfXsmtuI-1662541834340)(C:\Users\Cherish\AppData\Roaming\Typora\typora-user-images\image-20220907104505001.png)]


    const与非const对象与成员函数之间的相互调用关系

    对象与函数之间

    1. const对象可以调用非const成员函数吗? F–权限放大

    2. const对象可以调用const成员函数吗? T–权限缩小

    3. const成员函数内可以调用其它的非const成员函数吗?

      F–权限缩小

    4. const成员函数内可以调用其它的const成员函数吗?

      T–权限放大

    大家可以自己用程序去测试上面的关系!


    2.六大默认成员函数的另外两个

    const&运算符重载,const运算符重载

    实现:

    代码演示:

    #include
    using namespace std;
    
    Date* operator&() 
    {
    	return this;
    }
    
    const Date* operator&() const
    {
    	return this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    编译器生成的是完全够用的


    用途–可以不让别人访问其地址
    #include
    using namespace std;
    
    Date* operator&() 
    {
    	return nullptr;
    }
    
    const Date* operator&() const
    {
    	return nullptr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.友元

    友元函数

    由于类的封装性(访问限定符),类外部的函数是无法访问类的私有数据成员,但是有些时候我们又不得不让某个外部函数可以访问类的私有成员,友元函数就来了。就像是你配了一把你家钥匙给外面的人。

    所以建议不到万不得已是不要用友元的。


    友元函数的特性:
    1. 友元函数可以直接访问类的私有成员

    2. 它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声 明,声明时需要加friend关键字。

    友元函数的实现:
    #include
    using namespace std;
    
    class Date
    {
    	friend void F(Date d);
    
    public:
    
    	Date(int year = 2003, int month = 6, int day = 10)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    void F(Date d)
    {
    	d._year = 0;
    	d._month = 0;
    	d._day = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    当然了,上面的代码没有什么实际意义,只是为了大家可以理解以下友元函数的语法规则。


    友元类
    友元类的特性:
    1. 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

    2. 友元关系是单向的不具有交换性。 比如下述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time 类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

    3. 友元关系不能传递 如果B是A的友元,C是B的友元,则不能说明C时A的友元。

    友元类的实现:

    代码演示:

    #include
    using namespace std;
    
    class Time
    {
    	friend class Date;   // 声明日期类为时间类的友元类,那么日期类的成员函数全是时间类的友元函数,则在日期类中就直接访问Time类中的私有成员变量
    public:
    	Time(int hour = 0, int minute = 0, int second = 0)
    	{
    		_hour = hour;
    		_minute = minute;
    		_second = second;
    	}
    
    	void f(Date& d)
    	{
    		//d._year = 1;//error,Date类是Time类的友元类,而Time类不是Date类的友元类,无法访问其私有成员
    	}
    
    private:
    	int _hour;
    	int _minute;
    	int _second;
    };
    
    class Date
    {
    public:
    	Date(int year = 0, int month = 1, int day = 1)
    		
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    		_t._hour = 1;//在Date类中访问Time类的私有成员
    	}
    
    	void SetTimeOfDate(int hour, int minute, int second)//在Date类中访问Time类的私有成员
    	{
    		_t._hour = hour;
    		_t._minute = minute;
    		_t._second = second;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    	Time _t;
    };
    
    int main()
    {
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    4.实现自定义类型的cout<<,cin>>

    那么首先我们得理解内置类型的cout<<,cin>>的实现原理是什么?

    ####内置类型的cout<<,cin>>的实现原理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHfnHWJ3-1662541834341)(D:\gitee仓库\博客要用的截图\屏幕截图 2022-09-07 093110.png)]

    ​ 1.首先**istream,与ostream是两个类**,而**coutcin是两个类定义出来的对象**(当然了,这些对象在using namespace std里面就已经被定义出来了),这就是为什么你不写using namespace std他不让你使用cout的原因。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Ss74axO-1662541834342)(C:\Users\Cherish\AppData\Roaming\Typora\typora-user-images\image-20220907111457411.png)]

    ​ 2.其次呢,#include,是包含了std里面的一些运算符重载,其中就包括了,>> <<,当然了里面的实现就比较复杂,咱们不做深的讨论。


    自定义类型<<运算符重载的实现

    代码实现:

    #include
    using namespace std;
    
    class Date
    {
    
    public:
    	Date(int year = 2003, int month = 6, int day = 10)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void operator<<(ostream& out)//void operator<< (Date* this,ostream& out)
    	{
    		out << _year << "-" << _month << "-" << _day << endl;
    	}
    
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    
    int main()
    {
    	Date d1;
    	Date d2(2022,9,5);
    	
    	d1 << cout;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    运算符重载操作数位置的规则:

    ​ 可是这跟我们的使用习惯不一样,我们的习惯是将cout放在前面,d1放在后面

    ​ 运算符重载,操作数的位置是这样规定的:根据实参的位置而定,例如:假如有两个操作数,第一个操作数是第一个形参,第二个操作数是第二个实参。

    ​ 可是如果我们将<<运算符重载写成成员函数的话,第一个参数就会一直被this指针所占居,无法满足我们的需求。

    ​ 所以我们要将 <<运算符重载写成全局的,这样就可以cout去占到第一个操作数的位置了

    ​ 但是写成全局的是无法访问Date类里面的成员变量的,所以咱们要将这个函数声明成日期类的友元函数,这样才可以。


    代码实现:

    #include
    using namespace std;
    
    class Date
    {
    	friend ostream& Date::operator<<(ostream& out, const Date& d);
        
    public:
    	Date(int year = 2003, int month = 6, int day = 10)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void Print() const
    	{
    		printf("%d-%d-%d\n", _year, _month, _day);
    	}
    
    	
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    ostream& operator<<(ostream& out,const Date& d)//为了访问Date,必须还要将其声明成是Date的友元函数
    {
    	out <<d. _year << "-" << d._month << "-" << d._day << endl;
    	return out;
    }
    
    int main()
    {
    	Date d1;
    	Date d2(2022,9,5);
    	
    	cout << d1 << d2 << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    其中outcout的别名,只是为了在函数中进行区分,写成cout也是可以的


    自定义类型>>运算符重载的实现

    代码演示:

    #include
    using namespace std;
    
    class Date
    {
    	friend ostream& Date::operator<<(ostream& out, const Date& d);
    	friend istream& operator>>(istream& in,  Date& d);
    	//友元并不是说,operator是Date类里面的,可以理解成好朋友吧,然后可以访问朋友的成员变量
    public:
    	Date(int year = 2003, int month = 6, int day = 10)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void Print() const
    	{
    		printf("%d-%d-%d\n", _year, _month, _day);
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    istream& operator>>(istream& in, Date& d)
    {
    	in >> d._year >> d._month >> d._day;
    	return in;
    }
    
    ostream& operator<<(ostream& out,const Date& d)
    {
    	out <<d. _year << "-" << d._month << "-" << d._day << endl;
    	return out;
    }
    
    int main()
    {
    	Date d1;
    	Date d2(2022,9,5);
    	
    	cout << "输入年份d1,d2的值" << endl;
    	cin >> d1 >> d2;
    	cout << d1 << d2 << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    其中in是 cin 的别名,只是为了在函数中进行区分,写成cin也是可以的


    5.再谈构造函数

    初始化方式的多样化–初始化列表

    咱们先看看两种初始化的语法规则:

    1.首先是咱们原来的初始化方式–函数体内初始化
    #include
    using namespace std;
    
    class Date
    {
    public:
    	Date(int year = 0, int month = 1, int day = 1)
    		
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.新方法–初始化列表初始化
    #include
    using namespace std;
    
    class A
    {
    public:
    	A(int a = 0)
    		:_a(a)
    	{
    		cout << "A()" << endl;
    	}
    
    private:
    	int _a;
    };
    
    class Date
    {
    public:
    	Date(int year = 2022, int month = 9, int day = 6)
    	//初始化列表为成员变量定义的地方,创建的地方
    	:_year(year)
    	, _month(month)
    	, _day(day)
    	, _ref(_year)
    	, _aa(10)//引用必须在定义的时候就初始化,所以只能在初始化列表定义
    	, _n(10)
    	{
    		A aaa(20);
    		_aa = aaa;
    		
            _year = 2;
            _month = 1;
            _day = 1;
    	}
    
    	
    private:
    	int _year;
    	int _month;
    	int _day;
    	//那些必须在定义的时候就初始化的成员变量,必须在在初始化列表中,初始化
    	int& _ref;
    	
    	A _aa;
    
    	const int _n;
    
    };
    
    int main()
    {
    	Date d1(2003, 6, 10);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    初始化列表的特性
    1. 语法:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括 号中的初始值或表达式
    2. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
    3. 类中包含以下成员,必须放在初始化列表位置进行初始:
      • 引用成员变量

      • const成员变量

      • 自定义类型成员(该类没有默认构造函数)–

    有默认构造函数的时候,自定义类型是可以在函数体内初始化的,但是这样就麻烦了,不如直接在初始化列表初始化好。


    初始化列表的本质–重点

    初始化列表为成员变量定义的地方,所以严格点来讲,函数体内初始化并不是初始化,而是对成员变量的再处理。

    所以就会有引用成员变量const成员变量只能在初始化列表初始化,因为他们的语法规定是:在定义的同时就必须完成初始化,后面是无法再对他们的值进行处理的。

    _year _month _day他们在初始化列表初始化完成之后,还可以在函数体内进行在处理,所以他们可以不在初始化列表初始化。

    总结:建议都是用初始化列表初始化


    初始化列表初始化成员变量的顺序:

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

    下面有一个题,大家可以做一做:

    代码演示:

    #include
    using namespace std;
    
    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();
    }
    
    A.输出1 1
    B.程序崩溃
    C.编译不通过
    D.输出1 随机值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    explicit用法

    我们先来看看内置类型的隐式类型转换

    #include
    using namespace std;
    
    int main()
    {
        int i = 4;
        double j = 4.4;
        j = i;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    自定义类型也有隐式类型转换:

    class A
    {
        
    public:
    	A(int a = 0)
    		:_a(a)
    	{
    		cout << "A()" << endl;
    	}
        
    private:
    	int _a;
    };
    
    class Date
    {
    public:
    	Date(int year = 2022, int month = 9, int day = 6)
    		:_year(year)
    		, _month(month)
    		, _day(day)
    		, _aa(10)
    	{
    		A aaa(20);
    		_aa = aaa;//这个运用赋值运算符重载对_aa进行再处理
    		_aa = 30;//这个语法c++是支持的,这个是隐式类型转换,原理是 A tmp(30) + _aa = tmp
          			//构造+赋值重载
    				//编译器进行优化后把这句代码优化成了直接调用构造函数了
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    	A _aa;
    };
    
    int main()
    {
        Date d1(2003, 6, 10);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    隐式类型转换的实质是 A tmp(30) + _aa = tmp先构造,再赋值

    但是编译器优化之后,将隐式类型转换优化成了直接调用构造函数了,这个是可以用程序去验证的。


    当我们不想支持这种隐式类型转换的时候,可以在函数的前面加上explicit这个关键字

    explicit A(int a = 0)
    		:_a(a)
    	{
    		cout << "A()" << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    6.匿名对象

    我们来回顾以下,我们平时是如何来调用成员函数的?

    是创建一个对象,让对象取调用成员函数,而且不同的对象调用的是同一个函数。

    但是有时候的需求是:是需要调用一下函数,不需要使用对象,这个时候匿名对象就起作用了。

    匿名对象的特性
    1. 匿名对象的生命周期是这一行。
    2. 匿名对象也是调用构造函数来创建的。
    3. 当这一行结束之后,即对象销毁之后,就会调用其析构函数。

    匿名对象代码实现
    class Date
    {
    public:
    	Date(int year = 2022, int month = 9, int day = 6)
    		:_year(year)
    		, _month(month)
    		, _day(day)
    	{}
    
    	void Print()const
    	{
    		printf("%d-%d-%d\n", _year, _month, _day);
    	}
    
    	~Date()
    	{
    		cout << "~Date()" << endl;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    //匿名对象的生命周期在这一行
    int main()
    {
    	Date d1;
    	d1.Print();
    	Date d2(2003, 6, 10);
    	d2.Print();
    
    	Date().Print();//这里创建的是一个匿名变量,可以写一个析构函数看一下其生命周期
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    7.static成员

    static成员变量

    声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量

    静态成员变量是在类外面进行初始化的

    大家可以猜想以下这个A类的大小:

    #include
    using namespace std;
    
    class A
    {
    public:
    	void func()
    	{
    		//…………
    	}
    
    private:
    	static int _n;
    };
    
    int _n = 0;
    
    int main()
    {
    	cout << sizeof(A) << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    屏幕输出了一个1,1是用来占位的,说明静态成员变量不是定义在对象中的,而是定义在静态区的

    所以构造函数中的初始化列表并没有定义静态成员变量

    并且这个静态成员变量是所有权是类的所有成员,所有对象都可以访问,所有对象访问的都是一致的。


    static成员函数

    用static修饰的成员函数,称之为静态成员函数

    静态成员特性总结:
    1. 静态成员为所有类对象所共享,不属于某个具体的实例
    2. **静态成员变量必须在类外定义,定义时不添加static关键字 **
    3. 类静态成员即可用类名::静态成员或者对象.静态成员来访问 –类名::静态成员这是静态成员特有的访问方式
    4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
    5. 静态成员和类的普通成员一样,也有publicprotectedprivate3种访问级别,也可以具有返回值

    【问题】

    1. 静态成员函数可以调用非静态成员函数吗? F-权限放大,静态无this,非静态有this
    2. 非静态成员函数可以调用类的静态成员函数吗? T

    实战例子

    面试题:实现一个类,计算中程序中创建出了多少个类对象。

    需要一个计数器,为了保证这个计数器是类专有的,我们定义成静态成员变量

    class Date
    {
    public:
    	Date(int year = 2022, int month = 9, int day = 6)
    		:_year(year)
    		, _month(month)
    		, _day(day)//创建对象必然会调用构造函数,所以在构造函数中,对静态成员进行处理
    	{
    		++_count;
    	}
    
    	void Print()const
    	{
    		printf("%d-%d-%d\n", _year, _month, _day);
    	}
    
    
    private:
    	int _year;
    	int _month;
    	int _day;
    
    	static int _count;//静态成员变量
    };
    
    //int _count = 0;//error
    //注意,这样写,这个_count就毫无意义了,由于访问限定符的存在,只可以是Date类访问
    
    int Date::_count = 0;//初始化_count只有这一个后路
    
    int main()
    {
    
    	Date d1;
    	Date d2;
    	Date d3;
    	Date d4;
    	Date d5;
    	Date().Print();
    
    	cout << d5.Get_Count() << endl;
    	
    	cout << d1.Get_Count() << endl;
    	cout << d2.Get_Count() << endl;
    	Date();
    	cout << Date::Get_Count() << endl;//静态成员函数特有的调用方式,直接用类名访问
    	cout << Date().Get_Count() << endl;//这里由于创建了一个匿名变量_count的值又多了一次
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    8.c++11初始化的新玩儿法–了解即可

    #include
    using namespace std;
    
    class B
    {
    public:
    	B(int x = 0)
    	:_x(x)
    	{
    		
    	}
    
    private:
    	int _x;
    };
    
    class A
    {
    public:
    	A(int a = 10, double b = 3.14)
    		, _p(nullptr)
    		, __b(3)
    	{
    		_a = 100;
    	}
    
    private://这里在生命成员变量的时候给了缺省值,当初始化列表里面没有初始化处理时,就会使用缺省值
    	//顺序:先走初始化列表(当初始化列表没有对应的初始化,就会去走缺省值),再走函数体内
    	int _a = 9;
    	double _b = 5.5;
    	int* _p = nullptr;
    	int* a = (int*)malloc(sizeof(int)* 4);
    
    	B __b = 5;//隐式类型转换,编译器优化后是直接调用构造函数
    
    	static int n ;//但是静态成员变量不可以这样用,因为静态成员不在初始化列表定义,而是在全局区
    };
    
    int A::n = 1;
    
    int main()
    {
    	A aa;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    ​ 上面的写法与之前不同的地方是:在成员变量声明时给了缺省值,这个缺省值会在初始化列表中发挥作用。上述例子,初始化列表没有对成员a,成员b进行初始化处理,那么就会按照声明处给的缺省值,对成员a,b进行处理,并且指针类型,动态内存,自定义类型都可以这样用。

    ​ 大家可以对上述程序进行调式,了解其用法。


    9.内部类

    ​ 理解这句话即可–内部类天生外部类的友元类,而外部类不是内部类的友元类

    #include
    using namespace std;
    
    
    class Date
    {
    public:
    	Date(int year = 2022 ,int month = 9 ,int day = 8)
    		:_year(year)
    		, _month(month)
    		, _day(day)
    	{}
    
    
    	class Time
    	{
    	public:
    		void f(const Date& d)
    		{
    			_hour = 10;
    			_minute = 10;
    			_second = 10;
    			cout << d._year <<"-"<< d._month<< "-" << d._day << endl;
    			cout << _count << endl;//同样也可以访问静态的成员变量
    		}
    	private:
    		int _hour;
    		int _minute;
    		int _second;
    	};
    
    	void f2(Time t)
    	{
    		//cout << t._hour;//外部类不是内部类的友元
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    
    	static int _count ;
    };
    
    int Date::_count = 100;
    
    
    int main()
    {
    	Date d;//用Date创建一个对象
    	Date::Time t;//用Time创建一个对象,Time这个类是Date的内部类,访问方式是::
    	t.f(d);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55


    10.资料分享:

    截至到此,咱们的类与对象就彻底地结束了,博主一共总结了

    1. 类与对象(上)–介绍类,并且怎样写类

      (147条消息) c++入门语法收尾,类与对象(上)_月暂未满西楼的博客-CSDN博客

      (147条消息) c++类与对象(上)_月暂未满西楼的博客-CSDN博客

    2. 类与对象(中)–六大默认成员函数,以及运算符重载 重点

      (146条消息) 类与对象(中)–六大默认函数–重点_月暂未满西楼的博客-CSDN博客

    3. 类与对象(下)–类的一些其他语法

    下面给出几道对应的OJ题目,大家可以用来巩固知识点,如果你实现了日期类的各功能实现,那么这几个题目其实还好,没写的可以参考我的gitee2.3日期类各功能实现/2.3日期类各功能实现 · small_sheep/cplusplus0study - 码云 - 开源中国 (gitee.com)

    但是不要陷入到日期类里面去了,不要做每个题都把自己的日期类粘贴过来,想想其他方法。

    1.求1+2+3+…+n_牛客题霸_牛客网 (nowcoder.com)

    2.计算日期到天数转换_牛客题霸_牛客网 (nowcoder.com)

    3.日期差值_牛客题霸_牛客网 (nowcoder.com)

    4.打印日期_牛客题霸_牛客网 (nowcoder.com)

    5.日期累加_牛客题霸_牛客网 (nowcoder.com)

    OJ题目代码也已经放入gitee仓库中:

    类与对象OJ题源码.png · small_sheep/cplusplus0study - 码云 - 开源中国 (gitee.com)


    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pXGDqNrQ-1662541834343)(D:\gitee仓库\博客使用的表情包\给点赞吧.jpg)]
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sz8hHCbd-1662541834344)(D:\gitee仓库\博客使用的表情包\要赞.jpg)]

  • 相关阅读:
    移动硬盘被误删除了怎么找回呢?
    【机器学习】分类与预测算法的评价与优化
    应用程序主题生成很简单!界面控件DevExtreme有现成的主题生成器
    Java调用HTTPS接口,绕过SSL认证
    远程调试 idea配置remote debug、在远程服务器的程序中,添加JVM启动参数-Xdebug
    集卡实习总结
    从0搭建vue3组件库:自动化发布、管理版本号、生成 changelog、tag
    C S P - J / S 2021浙江省第二轮认证考生须知
    web前端-javascript-for循环练习(1-100所有奇数和,1-100所有7的倍数个数及总和,打印水仙花数,判断是否是质数)
    使用LIME解释CNN
  • 原文地址:https://blog.csdn.net/m0_64361907/article/details/126750029