继承
【本文目录】
- 1.继承的概念
- 2.继承的关系和方式
- 3.继承中的作用域
- 4.派生类的默认成员函数
- 5.继承的静态成员及友元
- 6.多继承的缺陷
- 7.继承和组合
继承的概念
【继承的目的】
类之间的复用
继承的概念
在C++中,继承就是一个类通过的继承的手段来使用另一类的成员对象和成员函数。
class Person{
public:
void show(){cout<<_Name<<endl;}
protected:
string _Name;
};
//学生类
class Student:public Person
{};
代码案例解析:类Student
通过公共继承的方式继承Person
类,在Student
类中可以使用Person
类中的成员对象和成员函数。
继承的关系和方式
继承的关系
在C++中,被继承的类是父类,也称为基类;继承另一个类的类为子类,也称为派生类。上面代码分析,其中被继承的类是Person
为基类;而继承其他类的对象是的类Studen
为派生类。
继承的方式
继承的方式有三种:public
、protected
、private
。
【public】
public
继承,是公共继承,在派生类内部,基类private
成员在派生类中是不能被访问 ,可以访问基类protected
和public
的成员对象和成员函数。公共方式继承下来对象访问权限不变。派生类对象仅能访问基类的公共访问符的成员对象和成员函数。是最常见的继承方式。
class Person
{
public:
Person()
{
_Name = "jack";
_Age = 18;
}
string _Name;
protected:
int _Age;
void show1() {}
private:
void Print()
{
cout << "_Name" << _Name << endl;
cout << "_Age" << _Age << endl;
}
};
class Student:public Person
{
public:
void show()
{
cout << _Name << endl;
cout << _Age << endl;
//Print(); //私有成员不可以访问
}
};
int main()
{
Student s;
s.show();
s._Name = "peter"; //公共继承仅能访问基类公共限定符对象 _Name
return 0;
}
【protected】
protected
继承,是保护继承。在派生类内部,基类private
成员在派生类中是不能被访问 ,可以访问基类protected
和public
的成员对象和成员函数。从基类中以保护继承下来的访问限定符的对象变成protected
访问限定符。派生类对象不可以访问基类成员对象和成员函数。
class Person
{
public:
string _Name;
protected:
int _Age;
private:
void Print()
{
cout << "_Name" << _Name << endl;
cout << "_Age" << _Age << endl;
}
};
class Student:protected Person //保护继承
{
public:
void show()
{
cout << _Name << endl;
cout << _Age << endl;
//Print(); //私有成员不可以访问
}
};
int main()
{
Student s;
s.show();
s._Name= "peter"; //派生类对象在保护继承下不能访问基类对象 ,会报错
return 0;
}
【private】
private
继承,是私有继承。在派生类内部,基类private
成员在派生类中是不能被访问 ,可以访问基类protected
和public
的成员对象和成员函数。从基类中以私有继承下来的访问限定符的对象变成private
访问限定符。派生类对象不可以访问基类成员对象和成员函数。
class Person
{
public:
string _Name;
protected:
int _Age;
private:
void Print()
{
cout << "_Name" << _Name << endl;
cout << "_Age" << _Age << endl;
}
};
class Student :private Person
{
public:
void show()
{
cout << _Name << endl;
cout << _Age << endl;
//Print(); //私有成员不可以访问
}
};
int main()
{
Student s;
s.show();
return 0;
}
【总结】
- 以公共方式继承,基类中的成员访问权限不变
- 基类中的私有成员不可以访问
- 建议使用公共方式继承
- 如果基类对象不想派生类对象在类外访问,而要派生类可以继承,基类成员可以定义为
protacted
访问权限。 - 当你省略不写继承方式时,
class
的默认继承方式是private
;struct
的默认继承方式是public
派生类与基类赋值规则
在C++中,派生类和基类是可以直接进行赋值操作的。
【派生类给基类赋值】
class Person
{
public:
Person(const char* name = "jack", int age = 18)
{
_Name = name;
_Age = age;
}
protected:
string _Name;
int _Age;
};
class Student : public Person
{
public:
Student(const char* name="peter",int age = 18,int id = 0)
:Person(name,age)
,_StuID(id)
{}
protected:
int _StuID;
};
int main()
{
Student s("小李",19,012);
Person p("小红",16);
//将派生类对象赋值给基类对象
p = s;
return 0;
}
当派生类对象可以给基类对象/基类的指针/基类的引用赋值,这一操作会发生切割(切片)的操作,将派生类的继承基类的对象,赋值给基类对象。
- 基类不可以给派生类赋值
继承中的作用域
- 在继承中的作用域基类和派生类是独立的
- 若子类和父类中有同名的成员对象,父类的对象会子类被隐藏,这也叫重定义
- 若子类和父类中有同名的成员函数,子类也会隐藏父类的同名函数。
- 建议不要子类和父类尽量不需要同名的成员对象和成员函数
【成员变量隐藏】
class Person
{
public:
Person(const char* name = "jack")
:_name(name)
{}
protected:
string _name;
};
class Student :public Person
{
public:
Student(const char* name = "peter")
:_name(name)
{}
void print() { cout << _name << endl; } //会把父类的name隐藏
protected:
string _name;
};
int main()
{
Student s;
s.print();
return 0;
}
【成员函数隐藏】
class Person
{
public:
Person(const char* name = "jack")
:_name(name)
{}
void print() {
cout <<"Person(const char* name = \"jack\")" << _name << endl;
}
protected:
string _name;
};
class Student :public Person
{
public:
Student(const char* name = "peter")
:_name(name)
{}
void print() {
cout <<"Student(const char* name = \"peter\")" << _name << endl;
} //会把父类的print()隐藏
protected:
string _name;
};
int main()
{
Student s;
s.print();
return 0;
}
若不想被隐藏就在前加上个作用域 基类::成员函数/成员对象
class Person
{
public:
Person(const char* name = "jack")
:_name(name)
{}
void print() {
cout <<"Person(const char* name = \"jack\")" << _name << endl;
}
protected:
string _name;
};
class Student :public Person
{
public:
Student(const char* name = "peter")
:_name(name)
{}
void print() {
Person::print();
cout << Person::_name << endl;
cout <<"Student(const char* name = \"peter\")" << _name << endl;
} //会把父类的print()隐藏
protected:
string _name;
};
int main()
{
Student s;
s.print();
return 0;
}
派生类的默认成员函数
- 派生类继承的基类对象,会调用基类的构造函数和析构函数
- 派生类继承的基类对象,赋值和拷贝构造也会调用基类的赋值和拷贝构造函数
- 先构造父类对象,再构造子类对象
- 先析构子类成员,再析构父类成员
对于派生类的默认成员函数,其实就是一句话自己管自己的
class Person {
public:
//先构造父类,在构造子类
Person(const char* name = "jack", int age = 18)
:_Name(name)
,_Age(age)
{
cout <<"Person()" << endl;
}
Person& operator=(const Person& p)
{
cout << "Person& operator=(const Person& p)" << endl;
if (this != &p)
{
_Name = p._Name;
_Age = p._Age;
}
return *this;
}
//先析构子类,再析构父类
~Person()
{
cout << "~Person()" << endl;
}
protected: // 注意使用 protected , 私有private子类继承不可以访问
string _Name;
int _Age;
};
class Student :public Person{
public:
Student(const char* name = "jack", int age = 18, int Id = 0)
:Person(name,age) //调用父类的构造函数
, _StuID(Id) {
cout << "Student()" << endl;
}
Student& operator=(const Student& s)
{
cout << "Student& operator=(const Student& s)" << endl;
if (this != &s)
{
Person::operator=(s); //父类函数与子类函数同名会被子类给隐藏
_StuID = s._StuID;
}
return *this;
}
~Student()
{
cout << "~Student()" << endl;
}
protected:
int _StuID;
};
int main() {
Student s("peter",18);
Student s1;
s1 = s;
//Person p("aaa", 10);
return 0;
}
继承的静态成员及友元
继承中的静态成员
继承当中,基类的静态成员继承到派生类中,仅有一个对象,再派生类中修改静态成员的值,基类中的静态成员也会改变。
class A
{
public:
void Prtin_A() {
cout << "A" << endl;
cout << s_num << endl;
}
static int s_num;
};
int A::s_num = 0;
class B :public A
{
public:
void Prtin_B() {
cout << "B" << endl;
cout << s_num << endl;
}
};
int main()
{
A a;
a.s_num = 2;
a.Prtin_A();
B b;
b.s_num = 3;
b.Prtin_B();
B::s_num++;
b.Prtin_B();
a.Prtin_A();
return 0;
}
继承中的友元
继承中友元关系是不会被继承下去的,不可以访问基类的保护成员和私有成员。若想要友元函数访问基类保护成员和私有对象,需要在基类中声明为友元函数。
class B; // 要提前声明派生类
class A
{
public:
friend void Print(const A& a, const B& b);
protected:
int num = 1;
};
class B
{
public:
friend void Print(const A& a, const B& b);
protected:
int num = 2;
};
void Print(const A& a, const B& b)
{
cout << a.num << endl; //会报错 不可以访问基类中私有成员和保护成员 ,除非在基类中声明该函数为友元
cout << b.num << endl;
cout << "友元" << endl;
}
int main()
{
A a;
B b;
Print(a,b);
return 0;
}
菱形继承的缺陷
继承可以分为两种就是单继承和多继承
【单继承】
单继承就是对于一个类来说仅有一个父类。
【多继承】
多继承就是对于一个类来说可以有多个父类。
【菱形继承】
菱形继承也是多继承的一种
菱形继承缺陷
- 会造成数据冗余和二义性
【菱形继承缺陷的解决】
使用关键字virtual
虚拟继承可以很好解决数据的冗余和二义性。
为什么使用虚拟继承后反而d的大小变大了?
【内存对象模型】
分析内存对象模型,当使用虚继承时,D对象中a放到最底部,因为这个A是B和C的父类,那么B和C怎么找到A呢?这里生成两个指针分别存放到B和C中,这两个指针叫做虚基表指针,虚基表指针指向的叫做虚基表,虚基表中存放的是偏移量,B和C可以通过偏移量来找到A。
继承和组合
- public继承是is-a的关系,就是说每个类都是一个基类对象
- 组合是has-a的关系,假设B组合A,每个B对象中都有一个A对象
【继承和组合的区别】
- 继承是高耦合
- 组合是低耦合,代码维护性较好