【C++】 ——继承的三种方式比较以及继承中小的知识点

C++继承的一般语法为:

class  派生类名:[继承方式]  基类名
{
       派生类新增的成员
}

继承方式限定了积累成员在派生类中的访问权限,包括public(公有的)、private(私有的)和protected(受保护的)。此项是可选项,如果不写会默认为private

1、public、protected、parivate修饰类的成员

  • 类成员的访问权限由高到低依次为public–>protected–>private
  • public成员可以通过对象来访问,private成员不能通过对象访问
  • protected成员和private成员类似,也不能通过对象访问。
  • 当存在继承关系的时候,protected和private就不一样了:基类中的protected成员可以在派生类中使用,而基类中的private成员不能在派生类中使用

2、 public 、protected、private指定的继承方式说明

1)public继承

  • 基类中所有public成员在派生类中为public属性
  • 基类中所有protacted成员在派生类中为protected属性
  • 基类中所有private成员在派生类中不能使用

2)protected继承

  • 基类中所有public成员在派生类中为protected属性
  • 基类中的所有protected成员在派生类中为protected属性
  • 基类中所有private成员在派生类中不能使用

3)private继承

  • 基类中所有public成员在派生类中均为private属性;
  • 基类中所有protected成员在派生类中均为private属性
  • 基类中所有private成员在派生类中不能使用

从以上可以看出:继承方式中的public,protected,private是用来指明基类成员在派生类中的最高访问权限的。不管继承方式如何,基类中的private成员在派生类中始终不能使用(不能在派生类的成员函数中访问或者调用)。如果希望基类中的成员能在派生类中毫无障碍的使用,那继承方式就可以声明为public或者protected,只有那些不希望在派生类中使用的成员才声明为private

要注意一点:基类中的private成员虽然不能在派生类中使用,但是可以在派生类中继承,并且会占用派生类对象的内存,它只是在派生类中不可见,导致无法使用

以下是继承基类成员访问方式的变化:在这里插入图片描述

3、 基类与派生类对象赋值的转化

  • 派生类对象可以赋值给基类对象/基类指针/基类的引用
  • 基类对象不能赋值给派生类(派生类的范围不基类大)
  • 基类的指针可以通过强制类型转化赋值给派生类指针,但必须是基类的指针指向派生类的对象时候才是安全的
    代码:
#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;
class person
{
private:
	int age;
	string name;
};
class student:public person
{
private:
	int sno;
};
void test()
{
	student s;
	//1、子类的对象可以赋值给派生类的对象,指针或者引用
	person p1 = s;
	person *p2 = &s;
	person &p3 = s;
	//2、父类对象不能赋值给子类的对象
	person p4;
	s = p4; //报错,不能这样写
	//3、父类的指针可以通过强制类型转换赋值给子类的对象
	person *p5 = &s;
	student* s1 = (student*)p5;

}
int main()
{
	test();
	system("pause");
	return 0;
}

4、继承中的隐藏

  • 在继承体系中基类和派生类都有独立的作用域
  • 派生类和基类有同名成员,子类成员屏蔽掉父类对同名成员的直接访问,叫做隐藏,但是在子类中可以使用 基类::基类成员显示访问
  • 函数名相同,就构成隐藏
    代码:
class person
{
protected:
	string name = "Lucy";
	int age = 21;
};
class student :public person
{
public:
	void print()
	{
		cout << "name:" << name << endl;
		cout << "person:age:" << person::age << endl;  //表明要显示父类的age
		cout << "student:age:" << age << endl;
	}
private:
	int age = 20;
};

5、隐藏、重载、重写(覆盖)的区别

先单个的了解它们的概念:

1)重载
在同一可访问区内被声明的几个具有不同的参数列(函数名相同,参数类型、个数、顺序不同)的函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型

class A
{
public:
	void test(int i);
	void test(double i); //重载
	void test(int i, double j); //重载
	int test(int i); //不是重载,因为重载不关心返回值类型
};

2)隐藏
是指派生类的函数屏蔽了与其同名的基类函数,只要函数名相同,不管参数列表是否相同,基类函数都会被隐藏。

class A
{
public:
	void fun(double, int)
	{
		cout << "父类:fun(double,int)" << endl;
	}
};
class B:public  A
{
public:
	void fun(int)
	{
		cout << "子类:fun(int)" << endl;
	}
};
int main()
{
	B b;
	b.fun(1);
	b.fun(10.1, 1);  //错误,函数不接受两个形参
	system("pause");
	return 0;
}

3)重写(覆盖)
派生类中存在重新定义的函数,函数名,参数列表,返回值类型都相同,只有函数体不同,派生类调用时会调用派生类的重写函数,不回调用被重写函数。重写的积累中被重写的函数必须要用virtual修饰

class A
{
public:
	virtual void fun(int i)
	{
		cout << "基类:fun()" << endl;
	}
};
class B :public A
{
public:
	virtual void fun(int i)
	{
		cout << "派生类:fun()" << endl;
	}
};
int main()
{
	A a;
	A* a1 = new B();
	a1->fun(3);  //输出:派生类:fun();
	system("pause");
	return 0;
}

重载和重写的区别:

  • 范围区别:重写和被重写的函数在不同的类中,重载和被重载的函数在同一类中
  • 参数区别:重写与被重写的函数参数列表一定相同,重载和被重载的函数参数列表一定不同
  • virtual的区别:重写的基类必须要有virtual修饰,重载和被重载函数可以被virtual修饰

隐藏和重写,重载的区别:

  • 与重载的范围不同:隐藏和被隐藏的函数在不同的类中
  • 参数不同:隐藏与被隐藏函数参数列表相同,也可以不同,但函数名一定相同;当参数不同时,无论基类中的是否被virtual修饰,基类函数都是被隐藏,而不是被重写

6、友元与继承

友元是不能被继承的,基类友元不能访问子类私有和保护成员
友元函数可以直接访问类的私有成员,它是定义在类外的普通函数,但是在类内声明,声明的时候只需在友元的名称前机上关键字friend

#include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;

class person
{
public:
	friend void display(const person&p, const student&s);
	friend void function(person p);
private:
	string name;
};
class student :public person
{
protected:
	int sno;
};
void display( const person&p, const student&s)
{
	cout << p.name << endl;//报错了,基类友元不能访问子类私有和保护成员
	cout << s.sno << endl;  //报错
}
void function(person p)  //因为function声明为person的友元,所以可以访问类中的所有成员
{
	cout << p.name << endl;
}
原创文章 78 获赞 21 访问量 3538

猜你喜欢

转载自blog.csdn.net/Vicky_Cr/article/details/105576761