秒懂C++之类与对象(下)

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

 

一.static成员

测试:

二.explicit关键字

 三.友元(少用)

友元函数:

友元类:

四.内部类(少用)

五.(扩展)编译器的优化


 

一.static成员

测试:

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

namespace lj
{
	int count = 0;
}
	class A
	{
	public:
		A() { ++lj::count; }
		A(const A& t) { ++lj::count; }
		~A() {  }

	private:
		
	};
	A func()
	{
		A aa;//构造
		return aa;//拷贝构造
	}


	int main()
	{
	
		A aa;//构造
		func();
	
	
		cout << lj::count << endl;//3
	
		return 0;
	}

虽然count准确计算出了A类一共创建了3个对象,但由于全局的特性使其无法再接着计数其他类的对象。

我们尝试不让它作为全局变量,而是放在类里~

这样做会使得每一个创建的对象都有自己的count,无法进行计数叠加。

 所以下面引出了static成员进行辅助~

1. 静态成员 所有类对象所共享 ,不属于某个具体的对象,存放在静态区
2. 静态成员变量 必须在 类外定义 ,定义时不添加 static 关键字,类中只是声明
3. 类静态成员即可用 类名 :: 静态成员 或者 对象 . 静态成员 来访问
4. 静态成员函数 没有 隐藏的 this 指针 ,不能访问任何非静态成员
5. 静态成员也是类的成员,受 public protected private 访问限定符的限制

有两点需要注意:在类内由static修饰的变量不能带有缺省值,因为缺省值是给初始化列表准备的,而初始化列表是去初始化某个对象。但此时被修饰后的count不再是属于每个对象了,而是属于所属类~

因此只能用类内声明,类外定义来初始化。

若我们让count公有化,则可以正常使用count计数,无论是指定域还是指定对象都可以调用

若继续让count私有化,我们就得借助成员函数来帮我们调用了(成员函数是公有的)。

不过这样的成员函数只能靠对象才能调用,无法通过指定域调用

如果没有aa对象还有两种方案:

要么临时创立一个对象去调用成员函数,不过这样最后的count要减1,因为aa1是我们为了调用而被迫创建的。

要么使用匿名对象——生命周期只有这一行,优点只比有名对象(aa1)节省一行,当然同样要count-1。

不过这些都太麻烦,这也引出了下一个知识点:静态成员函数

静态成员函数中无论是指定域还是指定对象都可以去访问。

普通成员函数一般是通过对象来找到所属域最后访问count,也可以是传递对象地址通过隐藏this指针找到函数中的count。

不过静态成员函数因为没有隐藏this指针因此不能访问函数中的非静态成员变量。

就算是计算类中大小也不会把静态成员变量计入,因为不在对象内,而是静态区中。

总结:使用静态成员变量与静态成员函数会有全局的效果,只不过会被类域和访问限定符限制。

二.explicit关键字

构造函数不仅可以构造与初始化对象, 对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用
class A
{
public:
	A(int a)
		:_a(a)
	{

	}
private:
	int _a = 0;
};


int main()
{
	A aa1(1);
	A aa2(2);
	A aa3 = 3;
}

aa1与aa2都可以构造成功,那么aa3呢?

.

内置类型隐式类型转换,所以aa3最后也可以成功构造出来。恰好3为Int类型与构造函数中的形参一致才可以构造成功,若换其他的类型(例如int*)最终会因为没有匹配的构造函数导致对象创建失败。

当然这是没有用explicit关键字 的效果:当我们使用后发现它会让隐式类型转换失效,但它无法限制强制转换。

我们再来尝试一下多参的隐式类型转换 

 只有d1与d3是符合要求的,而d2那个是逗号表达式,因此初始化不合理。

 三.友元(少用)

友元函数:

如果一个全局函数想要访问类里面的私有成员,可以在类内放友元声明。例如我们前面学过的流插入,流提取运算符。

友元类:

即一个类可以去访问另一个类的私有成员~

class Time
{
   friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
中的私有成员变量
public:
 Time(int hour = 0, int minute = 0, int second = 0)
 : _hour(hour)
 , _minute(minute)
 , _second(second)
 {}
   
private:
   int _hour;
   int _minute;
   int _second;
};
class Date
{
public:
   Date(int year = 1900, int month = 1, int day = 1)
       : _year(year)
       , _month(month)
       , _day(day)
   {}
   
   void SetTimeOfDate(int hour, int minute, int second)
   {
       // 直接访问时间类私有的成员变量
       _t._hour = hour;
       _t._minute = minute;
       _t._second = second;
   }
   
private:
   int _year;
   int _month;
   int _day;
   Time _t;
};

四.内部类(少用)

顾名思义,就是在类的内部再定义一个类。

特性:
1. 内部类可以定义在外部类的publicprotectedprivate都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。
class A
{
private:
 static int k;
 int h;
public:
 class B // B天生就是A的友元
 {
 public:
 void foo(const A& a)
 {
 cout << k << endl;//OK
 cout << a.h << endl;//OK
 }
 };
};
int A::k = 1;
int main()
{
    A::B b;
    b.foo(A());
    
    return 0;
}

若B类定义为私有,那么类外的A::bb就无法构建了,只能在类内实例化。

五.(扩展)编译器的优化

赋值拷贝的前提是已存在的两个对象进行赋值。

拷贝构造是一个已存在对象去初始化另一个要创建的对象。所以aa3其实是拷贝构造~

下面则是编译器的优化规则:

 

熟悉这些规则只是帮助我们更好理解编译完成所显现的信息,让我们了解为什么有的构造信息会被省略,原因就在于编译器自带的优化~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值