C++ -- 类和对象(下)

往期回顾

C++ – 类和对象(上)
C++ – 类和对象(中1)
C++ – 类和对象(中2)

1. 再谈构造函数

在前面的文章里我写到了构造函数的相关内容,现在我要进行补充。

1.1 初始化和赋值

在构造函数函数体内的操作都叫赋值,赋值是多次的。在类成员变量声明时可以给其缺省值,这个过程叫初始化,是只能进行一次的。除给予缺省值外,类中还有一种初始化叫初始化列表

1.2 初始化列表

初始化列表的语法是在构造函数后,以一个冒号开始,逗号分隔成员变量,每个成员变量后的括号内放其初始值

class AT {
public:
	AT(int a,int t)
		:_a(a)
		,_t(t)
	{	}

private:
	int _a;
	int _t;
};

1.3 初始化列表的细节

  1. 本质可以理解为成员变量定义的地方
  2. 类中的成员变量可以在列表位置初始化也可以在函数体内,但这三种情况必须在列表位置初始化:无默认构造函数的自定义类型成员、const成员变量、引用
class AT {
public:
	AT(int a,int b)
		:_a(a)
		,_b(b)
		,_st(10)
	{

	}

private:
	int& _a;
	const int _b;
	Stack _st;
};
  1. 初始化列表中的初始化顺序即是成员变量的声明顺序,与位置无关。
class A
{
public:
	A(int a)
		:_a2(_a1)
		,_a1(a)
	{}

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

private:
	/*int _a2;
	int _a1;*/ // 1 随机值

	int _a1;
	int _a2;   // 1 1
};

int main() 
{
	A aa(1);
	aa.Print();

	return 0;
}
  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。实际中的顺序是先初始化列表再函数体,且二者在效率上初始化列表更优。
  2. 成员变量的缺省值其实就是给初始化列表的。

1.4 隐式类型转换

我想过这样一个问题:内置类型是怎么变为自定义类型?
后来我明白了这归功于构造函数,构造函数可以做到隐式类型转换

// 单参数构造函数
class A
{
public:
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}

private:
	int _a;
	int _a1;
	int _a2;
};

int main()
{
	A aa1(1);
	A aa2 = aa1;
	A aa3 = 3;
	const A& raa = 3;

	return 0;
}

在这里插入图片描述
aa1是构造函数,aa2是拷贝构造,aa3是拷贝构造3的临时对象,raa是引用3构造的临时对象。
在这里插入图片描述
这个过程中应该是先调用构造函数再调用拷贝构造函数啊,为什么运行后只有构造函数?其实是编译器优化了这个过程,编译器遇到连续的构造+拷贝构造会优化为直接构造
说完单参数构造函数,来看看双参数构造函数

class A
{
public:
	// 单参数构造函数
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	// 多参数构造函数
	A(int a1, int a2)
		:_a(0)
		,_a1(a1)
		,_a2(a2)
	{}

	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}

private:
	int _a;
	int _a1;
	int _a2;
};

int main()
{
	A aaa1(1, 2);
	A aaa2 = { 1, 2 };
	const A& aaa3 = { 1, 2 };

	return 0;
}

只需要注意多参数需要用大括号包括。

1.5 explicit关键字

用explicit修饰构造函数,将会禁止构造函数的隐式转换

class A
{
public:
	explicit A(int a)
	// ......

private:
	int _a;
	int _a1;
	int _a2;
};

2. static成员

2.1 概念

用static修饰的成员变量叫静态成员变量,修饰的成员函数叫静态成员函数。

2.2 特征

  1. 必须在类外定义,类中的只是声明
  2. 可用 类名::静态成员 或者 对象.静态成员 来访问
  3. 静态成员存储在静态区而不在类内
  4. 声明时不能给缺省值
  5. 不在类内所以没有this指针,不能访问任何非静态成员
  6. 会受到public、protected、private 访问限定符的限制
// 体会static的声明和定义
class A {
public:
	A() 
	{ 
		++_scount; 
	}

	A(const A& t) 
	{ 
		++_scount; 
	}

	~A() 
	{ 
		--_scount; 
	
	}
	static int GetACount() 
	{ 
		return _scount; 
	}

private:
	static int _scount;
};

int A::_scount = 0;

void TestA()
{
	cout << A::GetACount() << endl;
	A a1, a2;
	A a3(a1);
	cout << A::GetACount() << endl;
};

int main()
{
	TestA();

	return 0;
}

3. 友元

3.1 友元函数

  1. 友元函数可以访问类的所有私有和保护成员,但其不是类的成员
  2. 友元不能被const修饰
  3. 友元不能继承
  4. 一个函数可以是多个类的友元
  5. 友元可以定义在类内的任意位置,不受访问限定符限制

3.2 友元类

  1. 友元类的所有成员函数都是另一个类的友元
  2. 友元关系是单向的
  3. 不能继承

4. 内部类

4.1 概念

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

4.2 特征

  1. 内部类是外部类的友元
  2. 内部类可以定义在外部类的任意地方
  3. 内部类可以随意使用外部类的成员

5. 匿名类

不给对象名的对象就是匿名类,其特点是生命周期只在定义的这行,离开这行便会销毁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值