c++继承是如何工作的?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++继承是如何工作的?相关的知识,希望对你有一定的参考价值。
答:C++继承有三种方式:public、protected、private 指定继承方式
不同的继承方式会影响基类成员在派生类中的访问权限。
1、 public继承方式
基类中所有 public 成员在派生类中为 public 属性;
基类中所有 protected 成员在派生类中为 protected 属性;
基类中所有 private 成员在派生类中不能使用。
2、 protected继承方式
基类中的所有 public 成员在派生类中为 protected 属性;
基类中的所有 protected 成员在派生类中为 protected 属性;
基类中的所有 private 成员在派生类中不能使用。
3、 private继承方式
基类中的所有 public 成员在派生类中均为 private 属性;
基类中的所有 protected 成员在派生类中均为 private 属性;
基类中的所有 private 成员在派生类中不能使用。
扩展资料:
通过C++继承方式可以发现:
1、 基类成员在派生类中的访问权限不得高于继承方式中指定的权限。
例如,当继承方式为 protected 时,那么基类成员在派生类中的访问权限最高也为 protected,高于 protected 的会降级为 protected,但低于 protected 不会升级。
再如,当继承方式为 public 时,那么基类成员在派生类中的访问权限将保持不变。
也就是说,继承方式中的 public、protected、private 是用来指明基类成员在派生类中的最高访问权限的。
2、不管继承方式如何,基类中的 private 成员在派生类中始终不能使用(不能在派生类的成员函数中访问或调用)。
3、如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;只有那些不希望在派生类中使用的成员才声明为 private。
4、如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected。
注意,我们这里说的是基类的 private 成员不能在派生类中使用,并没有说基类的 private 成员不能被继承。
实际上,基类的 private 成员是能够被继承的,并且(成员变量)会占用派生类对象的内存,它只是在派生类中不可见,导致无法使用罢了。private 成员的这种特性,能够很好的对派生类隐藏基类的实现,以体现面向对象的封装性。
由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public。
参考资料来源:百度百科-C++继承
参考技术A 公有继承(public)、私有继承(private)、保护继承(protected)是常用的三种继承方式。1. 公有继承(public)
公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。
2. 私有继承(private)
私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
3. 保护继承(protected)
保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
下面列出三种不同的继承方式的基类特性和派生类特性。
public protected private
共有继承 public protected 不可见
私有继承 private private 不可见
保护继承 protected protected 不可见
在上图中:1)基类成员对派生类都是:共有和保护的成员是可见的,私有的的成员是不可见的。
2)基类成员对派生类的对象来说:要看基类的成员在派生类中变成了什么类型的成员。如:私有继承时,基类的共有成员和私有成员都变成了派生类中的私有成员,因此对于派生类中的对象来说基类的共有成员和私有成员就是不可见的。
为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。
对于公有继承方式
(1) 基类成员对其对象的可见性:
公有成员可见,其他不可见。这里保护成员同于私有成员。
(2) 基类成员对派生类的可见性:
公有成员和保护成员可见,而私有成员不可见。这里保护成员同于公有成员。
(3) 基类成员对派生类对象的可见性:
公有成员可见,其他成员不可见。
所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。
对于私有继承方式
(1) 基类成员对其对象的可见性:
公有成员可见,其他成员不可见。
(2) 基类成员对派生类的可见性:
公有成员和保护成员是可见的,而私有成员是不可见的。
(3) 基类成员对派生类对象的可见性:
所有成员都是不可见的。
所以,在私有继承时,基类的成员只能由直接派生类访问,而无法再往下继承。
对于保护继承方式
这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,对基类成员有不同的可见性。
上述所说的可见性也就是可访问性。
关于可访问性还有另的一种说法。这种规则中,称派生类的对象对基类访问为水平访问,称派生类的派生类对基类的访问为垂直访问。 参考技术B “继承”(Inheritance)是面向对象软件技术当中的一个概念,如果一个类A继承自另一个类B,就把这个A称为"B的子类",而把B称为"A的父类"。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。尽管子类包括父类的所有成员,但它不能访问父类中被声明成private 的成员.
继承是指一个对象直接使用另一对象的属性和方法。事实上,我们遇到的很多实体都有继承的含义。例如,若把汽车看成一个实体,它可以分成多个子实体,如:卡车、公共汽车等。这些子实体都具有汽车的特性,因此,汽车是它们的"父亲",而这些子实体则是汽车的"孩子"。
继承的目的:实现代码重用
派生类声明:
class 派生类名:继承方式 基类名
新增成员声明;
;
三种继承方式:
公有继承 public (原封不动)
保护继承 protected (折中)
私有继承 private (化公为私)
继承方式影响子类的访问权限:
派生类成员对基类成员的访问权限
通过派生类对象对基类成员的访问权限
同类事物具有共同性,在同类事物中,每个事物又具有其特殊性。运用抽象的原则舍弃对象的特殊性,抽取其共同性,则得到一个适应于一批对象的类,这便是基类(父类),而把具有特殊性的类称为派生类(子类),派生类的对象拥有其基类的全部或部分属性与方法,称作派生类对基类的继承。 参考技术C 1. 继承层次结构
继承体系是一种类似数据结构中树状的层次结构,在树状层次的继承体系中,始终脱离不了两个基本概念即基类与派生类。通常软件开发中,类可以作为两种方式存在。一种是作为单独存在的自定义类型参与程序处理。另一种则是使用类继承机制,类通常不是作为顶层基类出现就是作为基类的派生类而存在,而该派生类又可以作为其派生类的基类。
2. 继承体系简介
前面已经说明过,一般可以作为继承顶层的基类,或者作为基类的派生类存在。基类通常又称为父类,在软件系统中基类应该表示一类事物最通用的基本特性。例如上小节中的水果与苹果类的继承关系,水果类代表一类水果最常见的特性与操作。而苹果类则为水果的一种,具有具体更多的特性描述,通过继承下次需要增加另外一种水果类型会变得很方便。直接继承至水果类即可实现,这种分析方式在面向对象中有助于理解使用继承增加软件可扩展性的概念。
继承体系可以是多个基类,派生类继承至该多个基类,通常这种继承方式称为为多继承。另外一种方式即可以是单个基类,扩展至多个派生类并且多个派生类之间是在同一个层次上,互不相干,同时派生类又可以继续派生下去。
通常类继承机制的优势在于,现有不变的类类型基础之上创建新类,从而通过增加新类操作方法来达到添加新功能的目的。最常见的是库的应用开发中,开发者可以开发出一定范围内可重用的组件类。而这些组件类功能在应用时可以根据具体需要在不修改原类的基础上进行继承扩充。软件中类的继承层次分析方法应用很多,面向对象编程思想中掌握这种分层方法分析事物有助于设计出高可扩展性以及功能强大的软件系统。
3.单继承中的基类与派生类
单继承有着很广阔的运用,一般软件开发中通过继承现有功能类的方式设计出功能更强大的处理类。但是通常要求基类中封装一类事物中最普遍的属性与操作,供派生类继承并加入新的属性与操作进行扩展。下面依然通过水果类继承实例,加入操作方法与数据成员,了解继承体系中基类与派生类的使用。
4. 继承体系中的访问控制
单独类的访问控制在类章节中已经详细讲述过。但是由于继承拥有public、protected以及private三种方式,针对不同的继承方式,与类中访问控制结合起来,使得派生类的访问控制变得更加的复杂。
5. 继承体系中的构造与析构
继承概念前面基本已经有一个大致的轮廓。但是其中还涉及到一个重要的部分,即基类、派生类的构造函数与析构函数定义执行的情况,下面将会就该主题通过完整实例作详细讲述。
6.多重继承
继承除了从单个基类派生以外还可以从多个基类派生继承创建新类。这种继承方式称为多重继承,下面将会详细讲述多重继承的基本应用情况。
所谓多重继承,即派生类继承至多个基类,并且拥有其多个基类的成员。多重继承方式与单继承方式基本语法实现形式相同,仅仅是在继承实现时通过逗号分隔符隔开多个基类名,其一般形式如下。
派生类: public/protected/private 基类1,基类2…
派生类继承至多个基类,即派生类中拥有多个基类成员,当然基类的构造函数与析构函数除外。多重继承除了在定义形式上与单继承稍有不同,其余访问控制等操作运用基本与单继承一致。下面通过一个完整实例演示前面小节分析的软件学院机构人员继承层次,其中行政人员同时又可以是老师的身份部分就包括多重继承的运用,即该类同时具有行政人员类与教师类的特征。
多重继承派生类拥有多个基类的所有成员,即派生类拥有多个基类的特性。上述实例中根据软件学院人员机构分析,有一类人员同时兼顾着行政人员与教师的特性,此时分析继承层次时可以将其定义为多重继承的方式。
本实例中总共涉及三个类,Administrtion、Teacher与AdministrationTeacher分别表示行政人员类、教师类以及兼顾行政管理以及教师人员类。其中AdministrationTeacher类继承至Administrtion与Teacher类。
而Administration类中除了构造与析构函数同时还包含两个公开的接口成员分别为,设置行政人员编号与设置行政人员的姓名的方法,另外还包含两个私有数据成员,分别表示行政人员的编号与姓名。而Teacher类中同样包含两个公开接口成员分别为,设置教师人员编号以及教师人员姓名的方法,同时也包含两个私有数据成员分别表示教师编号与姓名。最终继承至前两个类的AdministrationTeacher类在继承所有的方法成员之外,还包含该类添加的公开成员方法用于打印所设置的编号以及姓名信息。
实例主程序中首先定义AdministrationTeacher类对象实例administrationTeacher,随后使用该对象实例调用其继承至基类的方法成员setAdministrationId、setAdministrationName、setTeacherId以及setTeacherName,并且根据传入实参设置该类中的保护型数据成员。由于基类中数据成员为保护类型,在继承类中依然保持保护类型的特性,那么最后对象实例administrationTeacher调用派生类自定义方法showAdministrationTeacherInfo,该方法中访问了基类中的保护成员,用于在屏幕上显示设置的成员信息。
多重继承在应用软件开发中并不常用。因为其继承方式的使用不当会增加软件复杂出错的可能性,同时也可以会影响软件处理的效率。最常见的当多个基类中拥有同个方法成员,而这些基类被派生类继承的方式又不相同时,处理会变得复杂难懂。所以除非必须的处理场景下,一般不建议使用。
C++进阶:继承
文章目录
前言
本篇文章介绍什么是继承,继承是C++语法中的核心部分,无论是实际应用和面试找工作都是重点内容。希望大家重视起来。
一、继承的概念和定义
1.1继承的概念
继承机制是面向程序设计时使代码复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触到的复用都是函数复用,继承是类设计层次的复用。
class Person
{
public:
void Print()
{
cout << "name:" << name << endl;
cout << "age:" << age << endl;
}
protected:
string name = "bidelovec"; // 姓名
int age = 18; // 年龄
};
// 派生类 继承方式 基类
class Student : public Person
{
protected:
int stuid; // 学号
};
class Teacher : public Person
{
protected:
int jobid; // 工号
};
int main()
{
Student stu;
Teacher tea;
stu.Print();
tea.Print();
return 0;
}
1.2 继承方式和访问限定符
小结
- 基类private成员在派生类中无论以什么样的方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里还是类外都不能访问private成员。
- 基类private成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出protected限定符是因为继承才出现的。
- 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
- 实际运用时一般使用都是public继承,几乎很少使用protected/private继承,也不提倡。因为protected/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
二、基类和派生类对象赋值转换
- 派生类对象(子类)可以赋值给基类的对象/基类的指针/基类的引用。但是基类的对象不能赋值给派生类对象。
- 基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须是基类的指针是指向派生类对象时才是安全的。
class Person
{
protected :
string name; // 姓名
string sex; // 性别
int age; // 年龄
};
class Student : public Person
{
public :
int No ; // 学号
};
void Test ()
{
Student sobj ;
// 1.子类对象可以赋值给父类对象/指针/引用
Person pobj = sobj ;
Person* pp = &sobj;
Person& rp = sobj;
//2.基类对象不能赋值给派生类对象
sobj = pobj;
// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
pp = &sobj
Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
ps1->No = 10;
pp = &pobj;
Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
ps2->No = 10;
}
三,继承中的作用域
- 在继承体系中基类和派生类都有独立的作用域。
- 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用基类::基类成员显示访问)
- 如果是成员函数的隐藏,只需要函数名就构成隐藏。
- 实际中在继承体系里面最好不要定义同名的成员。
// Student的num和Person的num构成隐藏关系,可以看出这样代码虽然能跑,但是非常容易混淆
class Person
{
protected :
string name = "小李子"; // 姓名
int num = 111; // 身份证号
};
class Student : public Person
{
public:
void Print()
{
cout<<" 姓名:"<<name<< endl;
cout<<" 身份证号:"<<Person::num<< endl;
cout<<" 学号:"<<num<<endl;
}
protected:
int num = 999; // 学号
};
void Test()
{
Student s1;
s1.Print();
};
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A {
public:
void fun()
{
cout << "func()" << endl;
}
};
class B : public A {
public:
void fun(int i)
{
A::fun();
cout << "func(int i)->" <<i<<endl;
}
};
void Test()
{
B b;
b.fun(10);
};
四,派生类的默认成员函数
1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2.派生类的拷贝构造函数必须调用基类的拷贝构造函数完成基类的拷贝初始化。
3.派生类的operator=必须要调用基类的operator=完成基类的复制
4.派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员。
5.派生类对象初始化先调用基类构造再调派生类构造。
6.派生类对象析构清理先调用派生类析构再调基类的析构。
五,继承与友元,静态
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
class Student;
class Person
{
public:
friend void Display(const Person& p, const Student& s);
protected:
string name; // 姓名
};
class Student : public Person
{
protected:
int stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p.name << endl;
cout << s.stuNum << endl;
}
void main()
{
Person p;
Student s;
Display(p, s);
}
基类定义了static静态成员,则整个继承体系里只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。
class Person
{
public :
Person ()
{
++ count ;
}
protected :
string name ; // 姓名
public :
static int count; // 统计人的个数。
};
int Person :: count = 0;
class Student : public Person
{
protected :
int stuNum ; // 学号
};
class Graduate : public Student
{
protected :
string seminarCourse ; // 研究科目
};
void TestPerson()
{
Student s1 ;
Student s2 ;
Student s3 ;
Graduate s4 ;
cout <<" 人数 :"<< Person ::count << endl;
Student ::count = 0;
cout <<" 人数 :"<< Person ::count << endl;
}
六,菱形继承以及菱形虚拟继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承:菱形继承是多继承的一种特殊情况
菱形继承的问题:菱形继承有数据应冗余和二义性的问题。
class Person
{
public :
string name ; // 姓名
};
class Student : public Person
{
protected :
int num ; //学号
};
class Teacher : public Person
{
protected :
int id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
string majorCourse ; // 主修课程
};
void Test ()
{
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a ;
a.name = "peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::name = "xxx";
a.Teacher::name = "yyy";
}
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
class Person
{
public :
string name ; // 姓名
};
class Student : virtual public Person
{
protected :
int num ; //学号
};
class Teacher : virtual public Person
{
protected :
int id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
string majorCourse ; // 主修课程
};
void Test ()
{
Assistant a ;
现在主要从:
1.基类成员对其对象的可见性:
2. 基类成员对派生类的可见性:
3. 基类成员对派生类对象的可见性:
#include <iostream>
class base{
public:
base(){
a = 0;
b = 1;
c = 2;
}
int a;
protected:
int b;
private:
int c;
};
class derived1 : public base{//public继承
public:
void fun(){//derived class可以访问public和protected member
std::cout<<base::a<<base::b<<std::endl;
// std::cout<<base::c;//不能访问
}
};
class derived2 : protected base{//protected继承
public:
void fun(){//derived class可以访问public和protected member
std::cout<<base::a<<base::b<<std::endl;
// std::cout<<base::c;//不能访问
}
};
class derived3 : private base{//private继承
public:
void fun(){//derived class可以访问public和protected member
std::cout<<base::a<<base::b<<std::endl;
// std::cout<<base::c;//不能访问
}
};
class derived4 : public derived3{//base的member不能被private继承的派生类的子类访问
public:
void fun(){
// std::cout<<a;
}
};
class derived5 : base{//默认private继承
void fun(){//derived class可以访问public和protected member
std::cout<<base::a<<base::b;
// std::cout<<base::c;//不能访问
}
};
int main(void)
{
base b1;
derived1 d1;
derived2 d2;
derived3 d3;
d1.fun();
d2.fun();
d3.fun();
std::cout<<b1.a;//base class Object只能访问public member
std::cout<<d1.a;//public继承时 derived class Object只能访问base class的public member
//std::cout<<d1.b<<d1.c;//不能访问
//std::cout<<d2.a<<d2.b;//protected继承时 derived class Object不能问访base class的member
//std::cout<<d3.a;//private继承时 derived class Object不能问访base class的member
return 0;
}
总结
以上就是今天的内容,介绍了继承,单继承,多继承,菱形继承等。以上就是今天的内容,希望大家有所收获。
以上是关于c++继承是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章