目录
继承的定义
基类对象和派生类对象的赋值转换
继承中的作用域
派生类与基类中成员的关系
派生类与默认成员函数的关系
继承与友元的关系
继承与静态成员的关系
面向对象的三大特性:封装,继承,多态。
继承作为三大特性之一,是使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。继承呈现了面向对象程序设计的层次结构,继承是类设计层次的复用。
格式的定义:
//A类为基类
class A{
public:
int a;
};
// B 继承于 A
class B : public A {
public:
int b;
};
class B : public A 这句中 :其中B类为派生类,也叫子类;A类为基类,也叫父类;public为继承方式。
注意:继承后父类(A类)的成员(成员函数+成员变量)都会变成子类(B类)的一部分,B类也有自己的数据和方法。这里体现出了B类复用了A类的成员。B类对象可以访问到这些数据和方法,但也要有权限问题。
举个例子:
//Person类为基类
class Person{
public:
string name;
int age;
void Print()
{
cout << name << " " << age << endl;
}
};
//Student类继承了Person
class Student : public Person{
private:
int stuid: // 学号
};
//Teacher类继承了Person
class Teacher : public Person{
private:
int jobid;//教师职工号
};
Student类和Teacher类继承Person类,这两个类继承了Person类中的数据 ( name和age ) 和方法 ( Print() ) 。
通过复用,两个派生类都可以使用基类的数据和方法,不用再写声明相同的数据和方法,节省了代码量。
上面提到了继承的方式为public,其实还有private继承方式和protected继承方式。
下图为继承方式和访问限定符的关系图:
总结如下:
1、基类private成员在派生类中是不可见的。不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里还是类外都不能去访问它。
2、若基类成员不想在类外直接被访问,但想要在派生类中访问,就定义为protected。protected限定符是因继承才出现的。
3、class中默认为private继承,struct中默认为public继承。但最好显示的写出继承方式。
4、public继承最为常用,private/protected继承只能在类中访问,作用不大。在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承。这也是C++面向对象的较坑的一点。
基类与派生类之间的赋值转换只能发生在公有继承中。
1、派生类赋值给基类
派生类对象可以赋值给 基类的对象/基类的指针/基类的引用。
这个行为我们可以把它叫做 切片,意思就是把派生类中基类的那部分切分开赋值给基类。如下图所示
class A
{
public:
int a;
int b;
int c;
};
class B : public A
{
public:
int d = 4; //B中继承了A的a b c
};
int main()
{
B b;
b.a = 1;
b.b = 2;
b.c = 3;
//下面的过程叫切片
//子类对象可以赋值给父类对象/指针/引用
A a1 = b;
A* a2 = &b;
A& a3 = b; // 注意a3不是b的别名,而是其中父类部分的别名
//a对象中的成员变量a b c分别等于1 2 3
return 0;
}
对象间的赋值,是将派生类从基类继承来的数据复制一份给基类对象。
指针间的赋值,是将基类的指针指向派生类的对象,但所指的范围只包括基类的数据。
引用间的赋值,引用在底层也是一个指针,所以原理指针赋值的原理基本相同。
2、基类 无法 赋值给派生类
b = a; //这是错的
3、基类的指针可以通过强制类型转换赋值给派生类的指针
基类的指针必须是指向派生类对象地址才是安全的。
//a:基类 b:派生类
A *a = &b;
B *b1 = (B*)a; //这样可以
//a:基类 b:派生类
A a1 = b //切片
A *a2 = &a1;
B *b1 = (B*)a2; //这种情况转换时虽然可以,但会存在越界访问的问题。
//只能访问到基类的数据,无法访问派生类数据,是乱码。
class A
{
public:
int x = 1;
int y = 2;
void fun()
{
cout << x << " " << y << endl; //打印结果为 1 2
}
};
class B : public A
{
public:
int x = 3; //B类中的成员x和A类中的成员X构成隐藏(重定义)
int z = 4;
void fun() //B类中的成员函数fun和A类中的成员函数fun构成隐藏(重定义),成员函数满足函数名相同就构成隐藏
{
cout << x << " " << z << endl; //打印结果为 3 4
cout << A::x << endl; //打印结果为 1 ,显示的调用父类的成员
}
};
class A
{
protected:
int x;
public:
A(int _x = 1)
:x(_x)
{
cout << "A()" << endl;
}
A(const A& a)
:x(a.x)
{
cout << "A(const A&)" << endl;
}
A& operator=(const A& a)
{
cout << "operator=()" << endl;
if (this != &a)
{
x = a.x;
}
return *this;
}
~A()
{
cout << "~A()" << endl;
}
};
class B : public A
{
protected:
int y;
public:
B(int _x = 1, int _y = 2)
:A(_x) //先调基类构造
, y(_y)
{
cout << "B()" << endl;
}
B(const B& b)
:A(b) //先调拷贝基类构造
,y(b.y)
{
cout << "B(const B&)" << endl;
}
B& operator=(const B& b)
{
cout << "operator=()" << endl;
if (this != &b)
{
A::operator=(b);
y = b.y;
}
return *this;
}
~B() // 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员
{
cout << "~B()" << endl;
}
};
友元关系不能继承(基类友元不能访问子类私有和保护成员)