C++初阶类和对象

Posted Huang_ZhenSheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++初阶类和对象相关的知识,希望对你有一定的参考价值。

目录

开篇首先看一道题

再谈构造函数

初始化列表

static成员

友元

内部类


开篇首先看一道题

设已经有A,B,C,D 4个类的定义,程序中A,B,C,D析构函数的调用顺序为:

class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
};
class B
{
public:
	B()
	{
		cout << "B()" << endl;
	}
	~B()
	{
		cout << "~B()" << endl;
	}
};
class C
{
public:
	C()
	{
		cout << "C()" << endl;
	}
	~C()
	{
		cout << "~C()" << endl;
	}
};
class D
{
public:
	D()
	{
		cout << "C()" << endl;
	}
	~D()
	{
		cout << "~D()" << endl;
	}
};
C c;
int main()
{
	A a;
	B b;
	static D d;
	return 0;
}

C是一个全局对象,D是一个局部静态对象,对象在整个程序运行的期间都存在,在main函数结束的时候才会销毁

构造顺序:

C(一定在main函数之前被定义出来,全局对象在main函数之前初始化)

A

B

D(静态的局部对象在第一次调用当前域函数的时候初始化的。第二次调用的时候不会初始化了)

析构顺序:

C最先定义,最后析构

A   B   D呢?

D是第一次调用当前域函数的时候初始化的 ,D虽然生命周期是全局的,但毕竟是一个局部的,局部的静态对象还是要比C先析构的

B  ——>  A ——>  D ——>  C

再谈构造函数

class A
{
public:
	A(int a = 0)
	{
		_a = a;
		cout << "A(int a = 0)" << endl;
	}
	A& operator = (const A& aa)
	{
		cout << "A& operator = (const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}
private:
	int _a;
};
class B
{
public:
	B(int a, int b)
	{
		/*A aa(a);
		_aa = aa;*/
		_aa = A(a);
		_b = b;
	}
private:
	int _b = 1;//内置类型
	A _aa;//自定义类型
};
int main()
{
	B b(10,20);
	return 0;
}

笔者暂时没太懂这里的输出,后续理解后在进一步完善,也请各位大佬多多指教! 

从上面可以看出初始化_aa代价很大,调用了以上那么多的函数

初始化列表

对于自定义类型成员,改用初始化列表初始化,可以提高效率

将上面代码改成初始化列表 

class B
{
public:
	B(int a, int b)
		:_aa(a)
	{
		_b = b;
	}
private:
	int _b = 1;//内置类型
	A _aa;//自定义类型
};

 只调用了一个构造函数,调用了带参的构造

尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化

 哪些情况必须使用初始化列表初始化呢?

1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
自定义类型成员(该类没有默认构造函数)

下面看这段代码的输出结果? 

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中出现的先后次序无关,实际中,建议声明的顺序和初始化的列表的顺序保持一致!!!

class A
{
public:
	A(int a)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& aa)
	{
		cout << "A(const A& aa)" << endl;
	}
private:
	int _a;
};
int main()
{
	A aa1(1);
	A aa2 = 2;
	return 0;
}

下面本质是一个隐式类型转换

A aa2 = 2

1,先用2去构造临时对象

2,再用这个临时对象去拷贝构造aa2

3,最终vs编译又进行了优化,优化会用2作为参数,直接去构造aa2

static成员

int countC = 0;
int countCC = 0;
class A
{
public:
	A()
	{
		countC++;
	}
	A(const A& a)
	{
		countCC++;
	}
};
A f(A a)
{
	A ret(a);
	return ret;
}
int main()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
	cout << countC << endl;
	cout << countCC << endl;
	return 0;
}

————>以上代码容易被改动,如下代码进行改造:

int countC = 0;
int countCC = 0;
class A
{
public:
	A()
	{
		_count++;
	}
	A(const A& a)
	{
		_count++;
	}
private:
	//声明
	int _a;            //存在定义出的对象中,属于某个对象
	static int _count;//存在静态区,属于整个类,也属于每个定义出来的对象共享
	                  //跟全局变量比较,他受类域和访问限定符限制,更好的体现封装,别人不能轻易的修改
};
//定义初始化
int A::_count = 0;

A f(A a)
{
	A ret(a);
	return ret;
}
int main()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
	cout << sizeof(A) << endl;
	return 0;
}

当声明为公有的时候,可以用一下几种方式进行访问:—>(具体的说明详见代码块里的注释) 

int countC = 0;
int countCC = 0;
class A
{
public:
	A()
	{
		_count++;
	}
	A(const A& a)
	{
		_count++;
	}
//private:
	//声明
	int _a;            //存在定义出的对象中,属于某个对象
	static int _count;//存在静态区,属于整个类,也属于每个定义出来的对象共享
	                  //跟全局变量比较,他受类域和访问限定符限制,更好的体现封装,别人不能轻易的修改
};
//静态成员变量不能在构造函数初始化,在全局位置定义初始化
int A::_count = 0;

A f(A a)
{
	A ret(a);
	return ret;
}
int main()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
	cout << sizeof(A) << endl;
	//属于整个类,也属于每个定义出来的对象共享
	cout << A::_count<< endl;
	cout << a1._count << endl;
	cout << a2._count << endl;
	return 0;
}

下面给一个静态成员函数————>(具体的说明详见代码块里的注释) 

int countC = 0;
int countCC = 0;
class A
{
public:
	A()
	{
		_count++;
	}
	A(const A& a)
	{
		_count++;
	}
	//静态成员函数
	//静态成员函数没有this指针,无法访问_a
	static int GetCount()
	{
		return _count;
	}
private:
	//声明
	int _a;            //存在定义出的对象中,属于某个对象
	static int _count;//存在静态区,属于整个类,也属于每个定义出来的对象共享
	                  //跟全局变量比较,他受类域和访问限定符限制,更好的体现封装,别人不能轻易的修改
};
//静态成员变量不能在构造函数初始化,在全局位置定义初始化
int A::_count = 0;

A f(A a)
{
	A ret(a);
	return ret;
}
int main()
{
	A a1 = f(A());
	A a2;
	A a3;
	a3 = f(a2);
	cout << sizeof(A) << endl;
	//单纯的只想用一个对象去调用
	A ret;
	cout << ret.GetCount()-1 << endl;
	//下面就是匿名对象的一种使用场景
	cout << A().GetCount() - 2 << endl;//A()匿名对象
	cout << A::GetCount()-2 << endl;//使用静态的成员函数突破类域进行调用
	return 0;
}

总结:

static的作用:

C

1.修饰全局变量和全局函数,改变链接属性,只在当前文件可见

2.修饰局部变量,改变声明周期

C++

静态成员变量不能在构造函数初始化,在全局位置定义初始化

————》》》

1. 静态成员函数可以调用非静态成员函数吗
2. 非静态成员函数可以调用类的静态成员函数吗

友元

问题:现在我们尝试去重载operator<<,然后发现我们没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

(详见注释)

class Date
{
	//友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);//这里不能加const
public:
	Date(int year = 0, int month = 0, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
//为了让cout在第一个参数,左左操作数,我们就只能写成全局的
//其次就是operator<<搞成友元,可以在类中访问私有,但是operator<<不是必须友元,还有其他方式
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)//这里不能加const
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1,d2;
	//cin >> d1;//流提取
	//cout << d1;//流插入
	//运算符重载,运算符有几个操作数,重载函数就有几个参数
	//如果是两个操作数,左操作数是第一个参数,右操作数是第二个参数
	//operator<<写成成员函数,this指针默认占据了第一个位置,对象就要做做操作数
	//那么用起来就不符合流特性,虽然可以用,但是不符合运算符原来的用法和特性
	/*d1.operator<<(cout);
	d1 << cout;*/
	cin >> d1 >> d2;
	cout << d1 << d2;
	return 0;
}

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。 

额外的补充点: 

class A
{
public:
	A(int a = 0)
	:_a(a)
	{}
private:
	int _a;
};
class B
{
public:
	B()
		: _p(nullptr)
		, _aa(100)
	{}
private:
	//给的是缺省值,这里不是定义
	//这里只是声明,所以这里不是初始化
	int _b = 0;
	int* _p = (int*)malloc(sizeof(int)* 10);
	A _aa = 10;
	//静态变量不能在这里给缺省值,因为静态成员不在构造函数初始化
	//要在类外面全局位置定义初始化
	//static int _n = 10;
};
int main()
{
	B bb;
	return 0;
}

内部类

class A
{
private:
	static int k;
	int h;
public:
	//B天生就是A的友元
	class B
	{
	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;
}

以上是关于C++初阶类和对象的主要内容,如果未能解决你的问题,请参考以下文章

C++ 初阶类和对象

C++ 初阶类和对象

C++初阶类和对象

C++初阶类和对象

C++初阶---类和对象

C++初阶第四篇——类和对象(上)(类的定义+封装+this指针)