C++类和对象(下)

📋 个人简介

  • 💖 作者简介:大家好,我是菀枯😜

  • 🎉 支持我:点赞👍+收藏⭐️+留言📝

  • 💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️

    v2-af3cfbda99d44f8eb114cc6c74c92253_720w

前言

通过前面一段的时间,我们已经结束了类和对象的大部分内容。剩下的时间呢,我们来解决掉最后一点点的内容,C++类和对象的知识就结束了。

初始化列表

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

既然有了构造函数可以对成员变量进行赋值,那么为什么C++中还需要初始化列表呢?

首先我们来看这样一种场景。

class A{
public:
    A(int a, int& b, int c){
        _a = a;
        _b = b;
        _c = c;
    }
private:
    int _a;
    int& _b;
    const int _c;
};

我们定义了一个A类,A中含有a,b,c三个成员变量。其中b和c都有一个共同的特性,那就是他们只能在被定义的时候初始化,而无法再次被赋值,那么我们原本的构造函数可以为他们两个赋值吗?

1653034188517

我们运行起来看看结果:

int main(){
    int a = 1, b = 2, c = 3;
    A test(a,b,c);
    return 0;
}

image-20220524142047973

我们发现编译失败,编译器不允许我们这样做,那么如何解决这个问题呢?

这个时候,初始化列表就可以发挥它的作用了。

class A {
public:
	A(int a, int& b, int c)
		:_a(a),                      //初始化列表
		_b(b),
		_c(c)
	{	}
    
    void Print(){
        cout << _a <<" " << _b << " " << _c << endl;
    }
private:
	int _a;
	int& _b;
	const int _c;
};

这个时候,我们再来运行一下程序来看看结果。

int main() {
	int a = 1, b = 2, c = 3;
	A test(a, b, c);
	test.Print();
	return 0;
}

image-20220524143409274

注意事项

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    • 引用成员变量
    • const成员变量
    • 无默认构造函数的自定义类型
  3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。

static成员

在C语言中我们有static修饰的变量,我们都知道有static修饰的变量被创建后,会将变量的值存储到静态区。程序结束时,此变量才会被销毁。

在C++中呢,我们也有static这个关键字,并且产生了静态成员变量和静态成员函数。

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

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

我们来看看这个问题,下面这个类的大小该如何计算

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

    static int Count(){
        return _c;
    }
private:
    int _a;
    double _b;
    static int _c;
};

int A::_c = 0;

这个类中含有三个成员变量,其中一个为静态变量,如果将静态变量算入对象的大小中,那么这个类的大小就是20个字节,而如果不将此静态变量考虑到这个类中,那么这个类的大小就是16个字节,我们运行起来看看结果:

image-20220526084926864

为我们可以看到,结果为16,这就说明静态类型的成员变量并不是存储在对象的空间中,而是和C语言一样存储到静态区。

特性

  1. 静态成员为所有类对象所共享,不属于某个具体的实例。

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

    就像我们刚刚使用的A类中的c成员变量,他的定义在类的外面,类中的_c只是对这个静态成员变量的声明。声明方式如下:

    类型 类名::变量名 = 值;
    
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问

    int main(){
        A a;
        //(类名::静态成员访问)
        A::_c;
        //(对象.静态成员访问)
        a._c;
        return 0;
    }
    
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

  5. 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值.

友元

什么是友元

类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

为了解决上述问题呢,C++中引入了友元,友元可以突破封装,让我们直接去访问类中的私有成员。友元分为友元函数和友元类,书写方式如下:

// 友元函数
friend  返回值类型 函数名 (参数列表) 
// 友元类
friend class 类名()

和正常的函数和类区别就是在前面加上"friend"关键字即可,表明我是你的朋友,所以我可以访问你的私有成员。-

友元函数

我们先来看下面一段代码:

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

int main(){
    Date a;
    cout << a << endl;
    return 0;
}

现在我们是无法使用 cout << 去输出a的,我们需要将 <<进行重载,那么我们该如何实现呢?

1653034188517

这个时候我们就需要用到友元函数来实现这个了。注:下面代码中,ostream为cout 的类型。

class Date{
    //在类中声明友元函数
    friend ostream& operator<<(ostream& out, const Date& a);
public:
    Date(int year = 1, int month = 1, int day = 1)
        :_year(year),
    	 _month(month),
    	 _day(day)
         {}
private:
    int _year;
    int _month;
    int _day;
};

//类外实现函数
ostream& operator<<(ostream& cout, const Date& a){
    cout << a._year << "-" << a._month << "-" << a._day;
	return cout;    
}

int main(){
    Date a;
    cout << a << endl;
    return 0;
}

image-20220526094907254

此时友元就可以帮助我们突破封装,让 operator<< 函数直接去访问Date类中的私有成员变量。

友元类

友元类和友元函数形式上差不多,就是在类名前加上friend即可。

class Date{ 
private:
   	int _year;
    int _month;
    int _day;
    Time _t
};

class Time{
    friend class Date;
private:
    int _hour;
    int _minute;
    int _second
};

此时Date类就是Time类的友元类,Date可突破Time的封装直接去访问Time类中的私有成员。

class Time {
	friend class Date;
private:
	int _hour;
	int _minute;
	int _second;
};

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

	void Print(int hour, int minute, int second) {
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
		cout << _year << " " << _month << " " << _day << " " 
			<< _t._hour << " " << _t._minute << " " << _t._second << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};


int main() {
	Date a;
	a.Print(10, 12, 50);
	return 0;
}

输出结果如下:

image-20220605114011734

在Date类里面可将Time类中的私有成员进行改变。

注意:

友元不具备传递属性,比如A是B的朋友,B是C的朋友,A并不能直接去访问C中的私有成员,而需要通过B来访问C。

友元是单向的,A是B的友元,那么A就可以直接去访问B中的成员,但是B无法去访问A中的成员。

内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的 类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。

class A
{
private:
	int h;
public:
	class B
	{
	public:
		void Print(const A& a)
		{
			cout << a.h << endl;//OK
		}
	};
};

此时B就是A的内部类。B天生就是A的友元,它可以突破A的封装直接去访问A中的所有成员。

特性:

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
  3. sizeof(外部类)=外部类,和内部类没有任何关系。

结语

1647941444633

欢迎各位参考与指导!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

。菀枯。

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

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

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

打赏作者

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

抵扣说明:

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

余额充值