目录:
继承(inheritance
)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量) ,这样产生新的类,称派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用,继承是类设计层次的复用。
下面我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/
电话/年龄等成员变量,都有identity身份认证的成员函数,设计到两个类里面就是冗余的。
当然他们也有一些不同的成员变量和函数,比如老师独有成员变量是职称,学生的独有成员变量是学号;学生
的独有成员函数是学习,老师的独有成员函数是授课。
class student
{
public:
//进入校园、图书馆和实验室等需要二维码等身份验证
void identity()
{
//---
}
void study()
{
}
protected:
string _name = "peter"; // 姓名
string _address; // 地址
string _tel; // 电话
int _age = 18; // 年龄
int _stuid; // 学号
};
class Teacher
{
public:
// 进入校园/图书馆/实验室刷二维码等身份认证
void identity()
{
// ...
}
// 授课
void teaching()
{
//...
}
protected:
string _name = "张三"; // 姓名
int _age = 18; // 年龄
string _address; // 地址
string _tel; // 电话
string _title; // 职称
};
int main()
{
return 0;
}
下面我们就将这些公共的成员都放到person类中,student和teacher都继承person这样他们就可以直接服用这些成员, 就不再需要重复的定义这些变量和函数了, 省去了很多的麻烦
class Person
{
public:
// 进入校园/图书馆/实验室刷二维码等身份认证
void identity()
{
cout << "void identity()" <<_name<< endl;
}
protected:
string _name = "张三"; // 姓名
string _address; // 地址
string _tel; // 电话
int _age = 18; // 年龄
};
class Student : public Person
{
public:
// 学习
void study()
{
// ...
}
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
public:
// 授课
void teaching()
{
//...
}
protected:
string title; // 职称
};
int main()
{
Student s;
Teacher t;
s.identity();
t.identity();
return 0;
}
这个及时简单的继承实现方式。
类成员/继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
基类public成员 | 派生类public成员 | 派生类protected成员 | 派生类private成员 |
基类protected成员 | 派生类protected成员 | 派生类protected成员 | 派生类private成员 |
基类private成员 | 在派生类中不可见 | 在派生类中不可见 | 在派生类中不可见 |
基类private成员在派生类中无论以什么方式继承都是不可⻅的。这里的不可⻅是指基类的私有成员还是被继承到了派生类对象中。但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
实际上面的表格我们进行一下总结会发现,基类的私有成员在派生类都是不可⻅。基类的其他成员在派生类的访问方式(成员在基类的访问限定符,继承方式):
public>protected>private。
使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
// 实例演示三种继承关系下基类成员的各类型成员访问关系的变化
class Person
{
public :
void Print ()
{
cout<<_name <<endl;
}
protected :
string _name ; // 姓名
private :
int _age ; // 年龄
};
//class Student : protected Person
//class Student : private Person
class Student : public Person
{
protected :
int _stunum ; // 学号
};
class Person
{
protected :
string _name; // 姓名
string _sex; // 性别
int _age; // 年龄
};
class Student : public Person
{
public :
int _No ; // 学号
};
int main()
{
Student sobj ;
// 1.派生类对象可以赋值给基类的指针/引用
Person* pp = &sobj;
Person& rp = sobj;
// 派生类对象可以赋值给基类的对象是通过调用后面会讲解的基类的拷⻉构造完成的
Person pobj = sobj;
//2.基类对象不能赋值给派生类对象,这里会编译报错
sobj = pobj;
return 0;
}
- 在继承体系中基类和派生类都有独立的作用域。
- 派生类和基类中有同名成员,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。(在派生类成员函数中,可以使用基类::基类成员显示访问)
- 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
- 注意在实际中在继承体系里面最好不要定义同名的成员。
// 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; // 学号
};
int main()
{
Student s1;
s1.Print();
return 0;
};
经过前面相关知识的学习,现在我们就来做两道选择题,看看大家对前面的知识掌握的怎么样。
现在我们有下列这样的代码,请大家回答下列问题:
class A
{
public:
void fun()
{
cout << "func()" << endl;
}
};
class B : public A
{
public:
void fun(int i)
{
cout << "func(int i)" <<i<<endl;
}
};
int main()
{
B b;
b.fun(10);
b.fun();
return 0;
}
相信大家一定都尝试了这两道题吧 这两道题目的最终答案,我会在文章的最后揭晓哈,大家一定要记得去核对哈。
默认,其实就是指我们不写,编译器也会为我们自动生成的东西。那么在派生类中,这几个成员函数又会怎么生成呢?
operator=
必须调用基类的operator=
来完成基类的积累的复制,需要注意的是派生类的operator=
隐藏了基类的operator=
,所以显示调用基类的operator=
需要显示基类的作用域!virtual
的情况下,派生类析构函数和基类析构函数构成隐藏的关系。下面就是一段示例代码,大家可以拿下去自己验证一下前面的结论哟:
class Person
{
public :
Person(const char* name = "peter")
: _name(name )
{ cout<<"Person()" <<endl;
}
Person(const Person& p)
: _name(p._name)
{
cout<<"Person(const Person& p)" <<endl;
}
Person& operator=(const Person& p )
{
cout<<"Person operator=(const Person& p)"<< endl;
if (this != &p)
_name = p ._name;
return *this ;
}
~Person()
{
cout<<"~Person()" <<endl;
}
protected :
string _name ; // 姓名
};
class Student : public Person
{
public :
Student(const char* name, int num)
: Person(name)
, _num(num )
{
cout<<"Student()" <<endl;
}
Student(const Student& s)
: Person(s)
, _num(s ._num)
{
cout<<"Student(const Student& s)" <<endl ;
}
Student& operator = (const Student& s )
{
cout<<"Student& operator= (const Student& s)"<< endl;
if (this != &s) {
// 构成隐藏,所以需要显示调用
Person::operator =(s);
_num = s ._num;
}
return *this ;
}
~Student()
{
cout<<"~Student()" <<endl;
}
protected :
int _num ; //学号
};
int main()
{
Student s1 ("jack", 18);
Student s2 (s1);
Student s3 ("rose", 17);
s1 = s3 ;
return 0;
}
- 基类的构造函数私有: 派生类的构造必须要先调用基类的构造才行,如果基类的构造函数私有,派生类就无法调用基类的构造函数,那么派生类就没有办法实例化出对象。
- c++11 中增加了一个final关键字,final修饰基类,派生类就没有办法再被继承
下面是一些实例代码:
// C++11的方法
class Base final
{
public:
void func5() { cout << "Base::func5" << endl; }
protected:
int a = 1;
private:
// C++98的方法
/*Base()
{}*/
};
class Derive :public Base
{
void func4() { cout << "Derive::func4" << endl; }
protected:
int b = 2;
};
int main()
{
Base b;
Derive d;
return 0;
}
友元关系不能被继承,也就是说基类友元不能访问派生类私有成员和保护成员。
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;
}
int main()
{
Person p;
Student s;
// 编译报错:error C2248: “Student::_stuNum”: 无法访问 protected 成员
// 解决方案:Display也变成Student 的友元即可
Display(p, s);
return 0;
}
基类定义了static
静态成员对象,则整个继承体系里面只有这一个成员。无论派生出多少个派生类。都只有一个static
实例。
class Person
{
public:
string _name;
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
int main()
{
Person p;
Student s;
// 这里的运行结果可以看到非静态成员_name的地址是不一样的
// 说明派生类继承下来了,父派生类对象各有一份
cout << &p._name << endl;
cout << &s._name << endl;
// 这里的运行结果可以看到静态成员_count的地址是一样的
// 说明派生类和基类共用同一份静态成员
cout << &p._count << endl;
cout << &s._count << endl;
// 公有的情况下,父派生类指定类域都可以访问静态成员
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
Question One : 隐藏
Question Two: 编译报错
继承相关的问题,我们就系那个探讨到这里,我们下期再见拜拜!!