C++知识总结

一:类的public、protected、private修饰的成员访问权限

无继承情况下

类对其成员访问形式主要有一下两种:

  1. 内部访问:由类中成员函数对类的成员的访问
  2. 对象访问:在类外部,通过类的对象对类的成员的访问。

类的成员有public、protected、private三种访问属性,类的成员函数(内部访问)以及友元函数可以访问类中的所有成员(不考虑派生类继承的父类私有成员),但是在类外通过类的对象(对象访问),就只能访问类的公有成员。

有继承情况:

1.  Public修饰的成员变量和方法 在类的内部和类外部都能使用。

2.  Protected修饰的成员变量和方法在类的内部使用,在继承的子类中可以中可用。

3.  Private 修饰的成员变量、方法在类的内部使用,不能在类的外部使用,私有成员变量和方法即使被继承了在子类中也是不可使用的。

 

二:继承的public、protected、private方式,子类中的父类成员访问权限

继承:子类能继承所有的父类成员函数和变量。

  1. public继承:基类的public和protected成员的访问属性在派生类中保持不变,而基类的private 成员不可在派生类中访问。
  2. protected继承:基类的public、protecte成员都以protected出现在派生类中,而基类的private成员在派生类中不可以访问。
  3. private继承:基类的所有成员都以private身份出现在派生类中,而基类的私有成员在派生类中不可访问。

 

三:友元类

B是A的友元类,B中的所有函数都是A类中的友元函数。而友元函数是全局函数,同时可以访问A中的所有私有成员(变量和方法)。友元函数的声明位置与private/public/protected位置无关。

2.友元函数的使用

2.1友元函数的参数:

因为友元函数没有this指针,则参数要有三种情况:

2.1.1 要访问非static成员时,需要对象做参数;

2.1.2 要访问static成员或全局变量时,则不需要对象做参数;

2.1.3 如果做参数的对象是全局对象,则不需要对象做参数;

 

四: 继承与虚函数

1.  继承的构造函数和析构函数的执行顺序:在构造子类时,先执行父类构造函数,再执行子类构造函数,析构时,先执行子类的构造函数,再执行父类的构造函数。

因此,在实现多态(有继承关系的)时,析构函数通常是是虚函数。析构函数可以为虚函数,也可以不为虚函数

2. 为什么构造函数不能为虚函数

vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数。

虚函数对应一个vtale,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数,就需要到vtable中调用,可是对象还没有实例化,没有内存空间分配,如何调用。

3.  为什么基类的析构函数是虚函数呢? 

虚函数的作用:
  1.虚析构函数:在实现多态时,当一个类被作为基类并且该基类对派生类的对象进行操作,在析构时防止只析构基类而不析构派生类的状况发生。把基类的析构函数设计为虚函数可以在基类的指针指向派生类对象时,用基类的指针删除派生类对象,避免内存泄漏。

2.虚函数:在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数。 
  3.虚析构函数:析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该把函数定义为虚函数。

重载与重写:重载,函数名相同,返回值相同,但是函数参数列表不同,属于静态联编,未体现多态;重写体现在继承的关系中,函数名和函数参数相同,但是函数定义内容不同。当使用基类的指针指向子类的对象时,需要实现调用子类的函数体现出来。

五:静态成员

静态成员在类中只是声明,未定义,没有分配内存。它们属于类,所有对象的。必须在类外定义。静态成员函数只能访问静态成员,不能访问非静态成员,因为无this指针。

静态成员也是有属性的,访问权限受属性限制。

六:成员函数的重载、覆盖于隐藏(出自王道程序员面试宝典P160)

对于类层次中的同名成员函数来说,有3种关系:重载(overload)、覆盖(override)也就是虚函数、 隐藏(hide/oversee)也就重定义;

         重载:只有在同一个类定义中的同名成员函数才存在重载关系,并且函数的参数列表不相同(参数类型和个数不同),与函数返回值类型无关。既不能出现函数参数的个数和类型均相同,仅仅依靠返回值类型不同来区分。另外,重载和成员函数是否是虚函数无关。

重载的特征:

  1. 相同的范围(在同一个类中)
  2. 相同的函数名字
  3. 不同的参数列表
  4. Virtual关键字可有可无,与返回值也无关。

函数的存储是根据函数名和形参列表来区分的。

覆盖(重写):在派生类中覆盖基类中的同名函数,要求基类函数必须是虚函数,且:

  1. 与基类的虚函数有相同的参数个数;
  2. 与基类的虚函数有相同的参数类型
  3. 与基类的虚函数有相同的返回类型:或者派生类虚函数返回的指针(或者引用)类型是基类中被替换的虚函数所返回的指针类型的子类型(派生类型)。{基返回指针基类的是父类指针类型,派生类是子类指针类型。}

既基类中的函数有关键字virtual,派生类中的函数和基类中的函数完全一样的(除了函数实现内容),派生类中的virtual关键字可省略。

覆盖的特征:

  1. 不同的范围(分别位于派生类与基类)
  2. 相同的函数名字
  3. 相同的参数
  4. 基类函数必须有virtual关键字

重载与覆盖的区别如下:

  1. 覆盖是子类与父类之间的关系,是垂直关系;重载是同一个类中不同方法之间的关系,是水平关系;
  2. 覆盖要求参数列表相同,重载要求参数列表不同;覆盖要求返回类型相同,重载则不要求;
  3. 覆盖关系中,调用方法体是更加对象的类型类决定的,重载关系是根据调用时的实参表与形参表来选择方法体的。

隐藏(重定义):派生类中的函数屏蔽了基类中的同名函数。这些情况包括:

在派生类中调用基类的函数需要加入类名域。

  1. 两个函数参数相同,但基类函数不是虚函数。

2)两个函数参数不同,无论基类函数是否是虚函数,基类函数都会被屏蔽,和重载的区别在于两个函数不在同一个类中。

#include<iostream>
using namespace std;
class Parent
{
public:
	int p_a;
	static int m_a;
	Parent(int a = 0,int b = 1,int c=2,int *p=&m_a)
	{
		p_a = a;
		p_b = b;
		p_c = c;
		p_p = p;
	}
#if 0
	Parent(Parent& B) //浅拷贝构造函数
	{
		p_a = B.p_a;
		p_b = B.p_b;
		p_c = B.p_c;
		p_p = B.p_p;
	}
#endif 
	Parent(Parent &B) //深拷贝构造函数
	{
		p_p = new int;//分配一个堆空间用于存放p_p
		p_a = B.p_a;
		p_b = B.p_b;
		p_c = B.p_c;
		*p_p = *B.p_p;
	}

	void set(int a, int b, int c, int *p_p)
	{
		p_a = a;
		p_b = b;
		p_c = c;
		this->p_p = p_p;
		
	}
	virtual void print_a(void)
	{
		cout << "print parent a:" << p_a << endl;
	}
	void print_b(void)
	{
		cout << "print parent b:" << p_b << endl;
	}
	virtual void print_c(void)
	{
		cout << "print parent c:" << p_c << endl;
	}
	void show(void)
	{
		print_a();
		print_b();
		print_c();
		print();
	}
	static void setm_static_public(int a, int b, int c);
	virtual ~Parent()
	{
		//delete p_p;
	}

protected:
	int p_b;
	static int m_b;
private:
	int p_c;
	int *p_p;
	virtual void print()
	{
		cout << "Print Parent all memeber: " << endl;
		cout << " p_a=" << p_a <<  " p_b=" << p_b <<  " p_c=" << p_c << endl;
	}	
	static int m_c;
	friend void print_p(Parent &P);
	friend void print_m(void);
	friend void setm(int a, int b, int c);
	static void setm_static(int a,int b,int c);
};
int Parent:: m_a = 11;
int Parent:: m_b = 12;
int Parent:: m_c = 13;
void setm(int a, int b, int c)
{
	Parent::m_a = a;
	Parent::m_b = b;
	Parent::m_c = c;
}
void print_p(Parent &P)
{
	cout << "输出对象的非静态成员变量:" << endl;
	cout << "对象.p_a=" << P.p_a << " 对象.p_b=" << P.p_b << " 对象.p_c=" << P.p_c<<"*(对象.p_p)= " <<*P.p_p<< endl;
	cout << "输出静态成员变量:" << endl;
	cout <<"m_a="<< Parent::m_a<<" m_b=" << Parent::m_b <<" m_c="<< Parent::m_c << endl;
}
void print_m(void)
{
	setm(20, 21, 22);
	cout << Parent::m_a << Parent::m_b << Parent::m_c << endl;
}
void Parent::setm_static(int a, int b, int c)
{
	m_a = a;
	m_b = b;
	m_c = c;
}

void Parent::setm_static_public(int a, int b, int c)
{
	m_a = a;
	m_b = b;
	m_c = c;
}
/*
1. 友元函数可以访问类中的任意成员
2. 当友元函数访问的是非静态成员时,需要传对象参数
3. 访问静态成员时,不需要传递对象参数
4. 静态成员 也分私有和公有属性,只有公有属性的能在类外面使用。
5.
*/
void copy(Parent &A,Parent B)
{
	int a = 5;
	B.set(54,55,56,&a);
	A = Parent(B);
} 
#if 1
class Son:public Parent
{
public:
	int s_a;
	Son(int s_a, int s_b, int s_c)
	{
		this->s_a = s_a;
		this->s_b = s_b;
		this->s_c = s_c;
	};
	Son(int s_a, int s_b, int s_c, int sp_a, int sp_b, int sp_c) :Parent(sp_a, sp_b, sp_c)
	{
		this->s_a = s_a;
		this->s_b = s_b;
		this->s_c = s_c;
		//this->p_a=sp_a;//public 无论何种继承在子类内部可以使用,
						//如果需要对象访问则需要继承过来后是public属性
		//this->p_b=sp_b;//protected属性 无论何种继承在子类内部可以使用
		//this->p_c=sp_c;//无论何种继承都是是不可行的
	}
	void set(int a, int b, int c)
	{
		s_a = a;
		s_b = b;
		s_c = c;
	}
	void print_a(void)
	{
		cout << "print Son a:" << s_a << endl;
	}
	void print_b(void)
	{
		cout << "print Son b:" << s_b << endl;
	}
	virtual void print_c(void)
	{
		cout << "print Son c:" << s_c << endl;
	}
	void show(void)
	{
		print();
	}
	virtual ~Son(){}

protected:
	int s_b;
private:
	int s_c;
	void print()
	{
		cout << "Print Son all memeber: " << endl;
		cout << " s_a=" << s_a << " s_b=" << s_b << " s_c=" << s_c << endl;
	}	
};
#endif

int main()
{
	int a = 55;
	cout <<"调用默认形参的构造函数" << endl;
	Parent PA; //调用有参构造函数,默认参数A.p_a=0,A.p_b=1,A.p_c=2
	//当其他的构造函数存在是,编译器不会调用默认无惨构造函数
	//当赋值构造函数存在时,将不会自动调用系统的浅赋值构造函数
	cout <<"调用构造函数" << endl;
	Parent PB(6,7,8);
	cout << " ====括号运算符=====" << endl;
	Parent PC = (11,12,13);//这里是逗号运算符,取33; (= C++对等号 功能增强)然后编译器自动调用构造函数;PC.p_a=33,PC.p_b=1;PC.p_c=2;
	print_p(PC);
	cout << "=====类型转换======="<< endl;
	Parent PCC = 133;//(= C++对等号 功能增强)然后编译器自动调用构造函数;
	print_p(PCC);
	cout << "====类的赋值运算符===="<< endl;
	Parent PD = Parent(21, 22, 23);//先创建一个无名的Parent类,然后赋值给PD.
	cout << "=====类的赋值构造函数=======" << endl;
	Parent PE = Parent(PA);//匿名对象(匿名对象的去和留)对PE 对象初始化;只调用一次构造函数。
	//PE = PB; //赋值操作,把PB copy to PE
	cout << "PE 的成员:" << endl;
	print_p(PE);
	cout << "PA he PE:" << endl;
	PE.set(31, 32, 33,&a);
	cout << "PE 的成员:" << endl;
	print_p(PE);
	cout << "PA 的成员:" << endl;
	print_p(PA);
	cout << "PE PD===============:"<< endl;
	//PE = Parent(PD); //用一个函数来调用这句,就知道浅构造的区别了。
						//指针指向的内存还是PD指向的内存,
						//当PD的内存被释放后,则会出现问题。
	copy(PE,PD);//在函数调用中形参的指针p_p指向了另一个局部变量的内存地址
				//调用结束后,则释放了,指向了一个无效地址,不固定值
	cout << "PE 的成员:" << endl;
	print_p(PE);
	PE.set(35, 36, 37,&a);
	cout << "PE 的成员:" << endl;
	print_p(PE);
	cout << "PD 的成员:" << endl;
	print_p(PD);

	cout << "PA " << PA.p_a << endl; 
	//cout << PA.p_b << PA.p_c;//非公有属性会报错 
	//erro: private isnot uesed in class out(子类和派生类中都不能使用);	
	cout << "PA by print:" << endl;
	PA.print_a();
	PA.print_b();
	PA.print_c();

	print_p(PA);
	PA.set(15,16,17,&a);
	Parent::setm_static_public(25, 26, 27);
	print_p(PA);
	//print_m();
	PB.set(40, 41, 42,&a);
	setm(45, 46, 47);
	print_p(PB);
	//print_m();
	cout << "static m " << Parent::m_a << endl;
	//cout<< Parent::m_b << Parent::m_c<< endl; //非公有属性会报错
	//Parent::setm_static(11,12,13);非公有属性会报错

	print_m();
	setm(51, 52, 53);
	print_m();
	PA.show();
	cout << "========== 继承 和 虚函数 =============" << endl;
	Parent *P_parent;
	Son SA(60,61,62,63,64,65);
	Son SB(66,67,68);// p_a p_b p_c 为默认参数值 0,1,2
	cout <<"==========子类 SB===========" << endl;
	SB.show();
	cout << "++++++++++调用子类继承来的父类公有函数++++++++++++"<< endl;
	SB.Parent::show();
	cout << "======== 子类SA ============="<< endl;
	P_parent = &SA;
	SA.show();//属于重写,覆盖
	P_parent->print_a();
	P_parent->print_b();
	P_parent->print_c();
	cout << "====公有函数里面调用虚函数 ====" << endl;
	SA.Parent::show();//当有virtual的函数还是只执行son里面的
	cout << "+++++++++父类指针调用函数+++++++" << endl;
	P_parent->show();
	cout << "-------父类指针调用函数----------" << endl;
	P_parent = &PA;
	P_parent->show();
	cout << "===========派生类中继承父类的静态成员=================="<< endl;
	print_p(SA);
	cout << "=============派生类继承父类的公有成员函数=============="<< endl;
	SA.setm_static_public(25,26,27);
	print_p(SA);
	// protected:在类内部使用和子类中使用
	system("pause");
	return 0;

}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值