C++继承

继承的概念 :

继承:是一个类从另一个类获取成员变量和成员函数的过程(子类继承于父类)

派生:一个类派生给一个类(父类派生给子类)

继承的格式:

class   子类名 :  继承方式     父类名

{           内容          };

单继承:

class person//父类
{
public:
	int a;
	int b;
};

//class 子类名称 :继承方式 父类名称
class son :public person//子类继承于父类
{

};

继承的方式:

继承的方式有三种:public   protected   private

  1. 继承访问不了父类私有属性
  2. 但私有属性已经继承到了,只是被编译器隐藏了
  3. 静态成员属性不会被继承(属于全部对象)
  4. 创建子类时,构造函数的调用 先父类后子类
  5. 构造函数、析构函数、赋值操作符不能被继承

注意事项:无论什么继承,子类都可以访问父类中的公共和保护成员,但子类对象就只能访问子类的公共成员

公共继承:

class person//父类
{
public:
	int a;
protected:
	int b;
private:
	int c=10;
};
class son :public person//公共继承
{
public:
	son()
	{
		a = 10;
		b = 20;
		//c = 30;//报错,无法访问父类中的私有成员
	}
	void show()
	{
		cout << a << endl;
		cout << b << endl;
		//cout << c << endl;//报错,无法访问父类中的私有成员
	}

};
int main()
{
	son s;
	s.show();
	s.a = 30;//公共继承时,由于a是公有类型,所以子类对象可以访问公有类型数据
	//s.b = 30;//报错,公共继承时,由于b是保护类型,所以子类对象不可以访问保护类型数据
    //s.c=40;//报错,无法访问父类中的私有成员
	return 0;
}

保护继承:

class person//父类
{
public:
	int a;
protected:
	int b;
private:
	int c=10;
};
class son :protected person//保护继承,a和b都为 protected
{
public:
	son()
	{
		a = 10;//子类可以访问保护成员
		b = 20;
		//c = 30;//报错,无法访问父类中的私有成员
	}
	void show()
	{
		cout << a << endl;
		cout << b << endl;
		//cout << c << endl;//报错,无法访问父类中的私有成员
	}

};
int main()
{
	son s;
	s.show();
	//s.a = 30;//报错,保护继承时,由于a是保护类型,所以子类对象不可以访问保护类型数据
	//s.b = 30;//报错,保护继承时,由于b是保护类型,所以子类对象不可以访问保护类型数据
	//s.c=40;//报错,无法访问父类中的私有成员
	return 0;
}

私有继承:

class person//父类
{
public:
	int a;
protected:
	int b;
private:
	int c=10;
};
class son :private person//私有继承,a和b都为 private
{
public:
	son()
	{
		a = 10;//子类可以访问私有成员
		b = 20;
		//c = 30;//报错,无法访问父类中的私有成员
	}
	void show()
	{
		cout << a << endl;
		cout << b << endl;
		//cout << c << endl;//报错,无法访问父类中的私有成员
	}

};
int main()
{
	son s;
	s.show();
	//s.a = 30;//报错,私有继承时,由于a是私有类型,所以子类对象不可以访问私有类型数据
	//s.b = 30;//报错,私有继承时,由于b是私有类型,所以子类对象不可以访问私有类型数据
	//s.c=40;//报错,无法访问父类中的私有成员
	return 0;
}

继承中同名成员的处理:

  1. 成员变量同名
  2. 成员函数同名
  3. 成员函数同名,且父类中含有重载
  4. 静态成员的处理和非静态成员处理方式相同

处理方法为,子类的成员会覆盖掉父类中的成员。

同名使用作用域::也可以访问到父类的数据。

成员变量同名:

class person//父类
{
public:
	person(int p) :a(p){}
	int a;
	int b;
	int c=10;
};
class son :public person
{
public:
	son(int p,int p1,int p2):person(p),b(p1),c(p2){}
	int c;
	int b;
};
int main()
{
	son s(10,20,30);
	cout << s.a << endl;
	cout << s.b << endl;//同名使用的是自己的成员
	cout << s.c << endl;//同名使用的是自己的成员
	return 0;
}

 成员函数同名:

class person//父类
{
public:
	int a;
	int b;
	int c=10;
	void show()
	{
		cout << "person" << endl;
	}
};
class son :public person
{
public:
	void show()
	{
		cout << "son" << endl;
	}

};
int main()
{
	son s;
	s.show();//使用的是自己的show函数
	return 0;
}

 

 同名使用作用域也可以访问到父类的数据:

son.person::show();

class person//父类
{
public:

	void show()
	{
		cout << "person" << endl;
	}
};
class son :public person
{
public:
	void show()
	{
		cout << "son" << endl;
	}

};
int main()
{
	son s;
	s.show();//使用的是自己的show函数
	s.person::show();//使用作用域也可以访问父类的show函数
	return 0;
}

 同名且重载的成员函数:

class person//父类
{
public:

	void show()
	{
		cout << "person" << endl;
	}
	void show(int a)
	{
		cout << "person a" << endl;
	}
};
class son :public person
{
public:
	void show()
	{
		cout << "son" << endl;
	}
};
int main()
{
	son s;
	s.show();//使用的是自己的show函数
	s.person::show();//使用作用域也可以访问父类的show函数
	s.person::show(10);//使用作用域也可以访问父类的show函数的重载函数
	return 0;
}

单继承: 

 单继承的内存模型:

class person//父类
{
public:
	int a;
	int b;
};
class son :public person
{
public:
	int c;
};

 单继承时的内存计算:

子类内存计算:按照父类和子类中最大字节数据类型对齐,然后相加

注意:是先计算完父类,然后子类再加上父类

以下是32位下的计算

class person//父类
{
public://整体按照4对齐
	char a;
	char b;
};
class son :public person
{
public: //整体按照4对齐
	int c;
	char d;
};
相当于:父类4个字节,子类本身数据8个字节,子类占12字节

 

 举一个混合的例子:

class person//父类
{
public://整体按照8对齐
	double a;
	char b;
	int* p;
};
class son :public person
{
public: //整体按照8对齐
	int c;
	char d;
	int g[10];
};

 

特殊情况:当父类为空类时 (子类并不会加上父类 1个内存,该内存用来区分类对象)

class person
{
};
class son :public person
{
};

 

class person
{

};
class son :public person
{
public:
	int a;
};

单继承的构造和析构函数的调用:

生成普通对象或指针对象时:

  1. 构造函数的调用:先调用父类的 ,在调用子类的。
  2. 析构函数的调用与构造函数相反。
class person//父类
{
public:
	person(){cout << "person的构造函数" << endl;}
	~person(){cout << "person析构函数" << endl;}
};
class son :public person
{
public: 
	son(){cout << "son的构造函数" << endl;}
	~son(){cout << "son的析构函数" << endl;}
};
int main()
{
	son s;
	return 0;
}

 

生成一个 指针类型的对象:

class person//父类
{
public:
	person(){cout << "person的构造函数" << endl;}
	~person(){cout << "person析构函数" << endl;}
};
class son :public person
{
public: 
	son(){cout << "son的构造函数" << endl;}
	~son(){cout << "son的析构函数" << endl;}
};
int main()
{
	son *s=new son;
	delete s;
	return 0;
}

 关键知识点:

父类指针是可以接收子类对象

class person//父类
{
public:
	person(){cout << "person的构造函数" << endl;}
	~person(){cout << "person析构函数" << endl;}
};
class son :public person
{
public: 
	son(){cout << "son的构造函数" << endl;}
	~son(){cout << "son的析构函数" << endl;}
};
int main()
{
	person *s=new son;//用父类指针接收子类对象
	delete s;//调用的是父类析构函数
	return 0;
}

 

 父类指针指向子类对象时:

  • 当释放子类的指针对象时,会调用子类和父类的析构函数
  • 当释放父类的指针对象时,只会调用父类的析构函数
class person//父类
{
public:
	person(){cout << "person的构造函数" << endl;}
	~person(){cout << "person析构函数" << endl;}
};
class son :public person
{
public: 
	son(){cout << "son的构造函数" << endl;}
	~son(){cout << "son的析构函数" << endl;}
};
int main()
{
    son *s=new son;
	person *p = s;
	delete p;//调用person的析构函数
	return 0;
}

  父类指针指向子类对象,用子类对象释放内存时:

class person//父类
{
public:
	person(){cout << "person的构造函数" << endl;}
	~person(){cout << "person析构函数" << endl;}
};
class son :public person
{
public: 
	son(){cout << "son的构造函数" << endl;}
	~son(){cout << "son的析构函数" << endl;}
};
int main()
{
    son *s=new son;//定义一个子类指针
	person *p = s;//父类指针指向子类
	delete s;//释放子类内存
	return 0;
}

 

这里可以看出,释放子类对象会调用父类和子类的构造函数,释放父类的对象,只会调用父类的构造函数。

 注意在子类构造函数中的初始化问题:

调用父类构造函数的规则:

  1. 父类没有构造函数,使用无参构造
  2. 父类有无参和无参构造,优先使用无参构造
  3. 父类只有有参构造时,必须使用父类的有参构造
class person//父类
{
public:
	int a;
protected:
	int b;
private:
	int c=10;
};
class son :public person
{
public:
	son(int p,int p1,int p2):a(p),b(p1),c(p2){}//报错,a不是类son中的基类或非静态成员变量
	int c;
	int b;
};

注意:这种方法只能初始化本类成员,因为父类只有无参构造,而且构造函数这种方式不可以初始化,父类的成员,只能调用父类的构造函数来初始化父类的元素

class person//父类
{
public:
	int a;
protected:
	int b;
private:
	int c=10;
};
class son :public person//子类
{
public:
	son(int p,int p1,int p2){   //可以运行
		a = p;
		b = p1;
		c = p2;
	}
	int c;
	int b;
};

 父类只有有参构造时,必须使用父类的有参构造

class person//父类
{
public:
	person(int p) :a(p){}
	int a;
protected:
	int b;
private:
	int c=10;
};
class son :public person//子类
{
public:
	son(int p,int p1,int p2):person(p),b(p1),c(p2){}//必须使用父类的构造函数
	int c;
	int b;
};

派生类只能调用直接基类的构造函数,不能直接调用间接父类的构造函数

class person//父类
{
public:
	person(int p) :a(p) {}
	int a;
protected:
	int b;
private:
	int c = 10;
};
class son :public person//子类
{
public:
	son(int p, int p1, int p2) :person(p), b(p1), c(p2) {}//必须使用父类的构造函数
	int c;
	int b;
};
class man :public son
{
public:
	man(int a,int b,int c):person(a)//报错不能调用间父类的构造函数
};

 单继承中多层继承

模型为:

class person
{
public:
	person(int p) :a(p) {}
	int a;
};
class son:public person
{
public:
	son(int p,int p1) :b(p),person(p1){}
	int b;
};
class man :public son
{
public:
	man(int p, int p1, int p2):son(p,p1),c(p2){}
	int c;
};

处理方式和单继承相同。

继承多个父类:

继承模型:

class person//父类
{
public:
	person(int p) :a(p) {}
	int a;
};
class person1
{
public:
	person1(int p) :b(p) {}
	int b;
};
class son :public person,public person1//子类
{
public:
	son(int p, int p1, int p2) :person(p), person1(p1), c(p2) {}//必须使用父类的构造函数
	int c;
};

 继承多个父类的内存计算:

内存模型:

子类内存是两个父类的内存和子类内存的和进行字节对齐

class person//父类
{
public:
	person(char p) :a(p) {}
	char a;
};
class person1
{
public:
	person1(int p) :b(p) {}
	int b;
};
class son :public person,public person1//子类
{
public:
	son(char p, int p1, int p2) :person(p), person1(p1), c(p2) {}//必须使用父类的构造函数
	int c;
};

int main()
{
	son s(1, 2, 3);
	cout<<sizeof(s)<<endl;
	return 0;
}

注意,内存的计算与继承的先后有关

继承先后不同,数据存放的位置不同

class person//父类
{
public:
	person(char p) :a(p) {}
	char a;
};
class person1
{
public:
	person1(int p) :b(p) {}
	int b;

};
class son :public person,person1//继承先后位置不同
{
public:
	son(char p, int p1, int p2) :person(p), person1(p1), c(p2) {}
	char  c;
};

int main()
{
	son s(1, 2, 3);
	cout<<sizeof(s)<<endl;//结果为12
	return 0;
}
class person//父类
{
public:
	person(char p) :a(p) {}
	char a;
};
class person1
{
public:
	person1(int p) :b(p) {}
	int b;

};
class son :public person1,person//继承先后位置不同
{
public:
	son(char p, int p1, int p2) :person(p), person1(p1), c(p2) {}
	char  c;
};

int main()
{
	son s(1, 2, 3);
	cout<<sizeof(s)<<endl;//结果为8
	return 0;
}

继承多个父类时父类之间同名成员的处理

  1.  内存上,两个成员都存放在内存中
  2. 访问上,可以使用作用域来访问父类中的同名成员
class person//父类1
{
public:
	person(char p) :a(p) {}
	int a;
};
class person1//父类2
{
public:
	person1(int p) :a(p) {}
	int a;

};
class son :public person,public person1//子类
{
public:
	son(char p, int p1, int p2) :person(p), person1(p1), c(p2) {}
	char  c;
};

 1. 内存上,两个成员都存放在内存中

int main()
{
	son s(2, 3, 4);
    cout << sizeof(s) << endl;//结果为12
	return 0;
}

在上面已经解释过了,这里就不解释了 

 2.访问上,可以使用作用域来访问父类中的同名成员

int main()
{
	son s(2, 5, 3);
    s.a;//不可访问,存在二义性
	s.person::a;//可以访问
	s.person1::a;//可以访问
	return 0;
}

 继承多个父类时父类和子类之间同名成员的处理

  1.  内存上,两个成员都存放在内存中
  2. 访问上,创建子类对象时,默认使用子类的成员,可以使用作用域来访问父类中的同名成员
using namespace std;
class person//父类
{
public:
	person(char p) :a(p) {}
	int a;
};
class person1
{
public:
	person1(int p) :b(p) {}
	int b;

};
class son :public person,public person1//子类
{
public:
	son(char p, int p1, int p2) :person(p), person1(p1), a(p2) {}
	int a;
};
int main()
{
	son s(2, 3, 4);
	cout << sizeof(s) << endl;//结果为12
	cout << s.a << endl;//结果为4
	cout << s.person::a << endl;//结果为2
	return 0;
}

 

菱形继承和虚继承

菱形继承的模型:

class person
{
public:
	int _a;
};
class son :public person
{
public:
	int _b;
};
class son1 :public person
{
public:
	int _c;
};
class man :public son, public son1
{
public:
	int _d;
};

 

 这样的继承方式,子类中会有两份相同的person的数据,为了防止命名冲突和冗余数据问题。

虚继承:

c++提出了虚继承,使得在派生类中只保留一份间接基类的成员,使用virtual修饰继承方式

class person
{
public:
	int a;
};
class son :virtual public person//虚继承
{
public:
	int b;
};
class son1 :virtual public person//虚继承
{
public:
	int c;
};
class man :public son, public son1
{
public:
	int d;
};
int main(){
	man m;
	cout << sizeof(m) << endl;//结果为20
	return 0;
}

重点:虚继承内存的计算 

举一个例子:

class person
{
public:
	int a;
};
class son :virtual public person//虚继承
{
public:
	int b;
};
int main() {
	cout << sizeof(person) << endl;//结果为4
	cout << sizeof(son) << endl;//结果为12
	return 0;
}

为什么sizeof(son)的结果为12,

在虚拟继承时:(在微软的编译器中)会生成一个virtual base class table。编译器会安插一个指针来指向它,所以最后需要添加指针的大小,所以结果为12,当虚继承多个类时,就会从生成多少个指针。

class person
{

};
class person1
{

};
class person2
{

};
class son :virtual public person, virtual public person1,virtual public person2
{
};

int main() {
	cout << sizeof(son) << endl;//结果为12,包含3个指针
	return 0;
}

虚拟继承构造函数的调用:

  1.  先调用虚基类的构造函数,再调用非虚基类的构造函数
  2. 生成间接派生类时,直接派生类的构造函数与继承的顺序有关,先继承先调用
class person//虚基类
{
public:
	person()
	{
		cout << "person的构造函数" << endl;
	}
};
class person1:virtual public person//直接派生类1
{
public:
	person1()
	{
		cout << "person1的构造函数" << endl;
	}
};
class person2:virtual public person//直接派生类2
{
public:
	person2()
	{
		cout << "person2的构造函数" << endl;
	}
};
class son :public person1,public person2//间接派生类
{
public:
	son()
	{
		cout << "son的构造函数" << endl;
	}
};

int main() {
	son s;//生成一个间接派生类的对象
	return 0;
}

友元函数和友元类

  1. 友元函数的格式:在函数前加 friend
  2. 友元函数的作用:可以使类外的函数访问到类的保护和私有成员
  3. 友元函数生成方法:
  • 全局函数   
  • 成员函数

全局函数:

class person
{
public:
	//把show2变为友元函数
	friend void show2(person &p);
	person(string name, int age)
	{
		this->name = name;
		this->age = age;
	}
	int age;
private:
	string name;
};
void show2(person &p)//普通友元函数,使用时形参需要指明对象
{
	//因为是友元函数,所以可以访问保护和私有变量
	p.age = 10;
	cout << "age的值:" << p.age << endl;
	p.name = "kkkkk";
	cout << "name=" << p.name << endl;
}

成员函数 :

class son;
class person
{
public:
	person();
	void show();
	son *son1;
};
class son
{
	friend void person::show();//父类成员函数做子类的友元函数
public:
	son();
	int age;
private:
	int height;
};
son::son()
{
	age = 10;
	height = 120;
}
person::person()
{
	son1 = new son;
}
void person::show()//友元函数的实现
{
	cout << "age=" << this->son1->age << endl;
	cout << "heighet=" << this->son1->height << endl;
}
void text()
{
	person p;
	p.show();
}
int main()
{
	text();
	return 0;
}

友元类:

使得一个类可以访问另一个类中的保护和私有成员

class son
{
	//把person类设置为友元类
	friend class person;
public:
	int age;
private:
	int height;
};
class person
{
public:
	void show()
	{
		this->s.age = 10;
		//访问son的私有成员
		this->s.height = 120;
	}

	son s;
};

友元的注意事项:

友元函数:

  1. 友元函数不是成员函数
  2. 友元函数没有this指针
  3. 友元函数会提高效率减少了类型检查和安全性检查,提高了程序的运行效率,但破坏了类的隐藏性封装性

友元类: 

  1. 友元关系不能被继承
  2. 友元关系是单向的,不具有交换性
  3. 友元关系不具有传递性
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值