为什么需要有继承:
代码复用与实现多态
1.继承的概念与定义
1.1继承的概念
继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许在其原有的类特性上进行扩展,增加功能,这样产上的新类称为派生类(子类)。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承是类设计层次的复用。
class Base//基类(父类)
{
public:
void Print()
{
cout << "Print()" << endl;
cout << _b << endl;
}
protected:
int _b = 10;
};
// 继承后父类的Base的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Derived复用了Base
// 的成员。下面我们使用监视窗口查看Derived对象,可以看到变量的复用。调用Print可以看到成员函数的复用。
class Derived:public Base//派生类(子类)
{
protected:
int _d = 20;
}
int main()
{
Base b;
Derived d;
b.Print();
d.Print();
return 0;
}
1.2继承定义
1.2.1 定义格式
Base是基类,也称父类,Derived是派生类,也称子类。
1.2.2 继承关系和访问限定符
继承方式:① public继承 ② protected继承 ③ private继承
访问限定符:① public访问 ② protected访问 ③ private访问
1.2.3 派生类中成员的访问权限变化
类成员 / 继承方式 | public 继承 | protected 继承 | private 继承 |
---|---|---|---|
基类的public成员 | public | protected | private |
基类的protected成员 | protected | protected | private |
基类的private成员 | private | private | private |
注意:从表格中可以看出在派生类(子类)的的访问权限是类成员与继承方式的最小权限。
访问权限大小排行:public > protected > private
总结:
1. 只要基类(父类)中的某个成员属性是 private ,那么它在类外还是派生类(子类)中都是不可见的,谁都无法访问。但是该成员还是被继承到了派生类(子类)中,只是无法访问。
2. 若想让该成员不能在类外被访问,但可以在派生类(子类)中访问,那么就得把成员权限设置成为 protected ,当继承权限为 public 或 protected 时,就可以实现这种现象,但当继承权限为private时,这种就不行了。
3. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
4. 我们一般情况下继承方式都是使用 public ,因为protetced/private继承下来的成员都只能在派生类(子类)的类里面使用,实际中扩展维护性不强。
2.基类与派生类的互相赋值转换
1. 派生类(子类)的对象可以赋值给基类(父类)对象,但是基类(父类)的对象不能赋值给派生类(子类)的对象;
2. 基类(父类)的指针可以指向派生类(子类)对象,基类(父类)的引用也可以指向派生类(子类)对象,反而派生类(子类)就无法指向基类(父类);
3. 虽然派生类(子类)无法指向基类(父类),但是可以通过强制类型转化指向基类(父类)。
· 为什么派生类可以赋值给基类,而基类无法赋值给派生类?
假设基类中成员变量有 a 和 b,通过继承在派生类同样有这两个变量,一般情况下派生中会独有成员变量(假设派生类中自己有一个变量 c),那么在赋值的时候,派生类就可完全满足基类成员变量的赋值,但是基类就不可以,它无法满足派生类独有的成员变量的赋值。
·为什么基类的指针和引用可以指向派生类,而派生类不行呢?
还是根据上面的例子,基类对象会有一段空间,而派生类的对象也会有一段空间,但是派生类的空间范围大于基类的空间范围,当基类使用指针或引用指向派生类的时候通过在派生类空间上截取是合法的,但是相反基类的空间就无法满足派生类指针或引用的截取。
3.继承中的重定义(同名隐藏)
1. 在继承体系中,基类与派生类拥有自己独立的作用域;
2. 若基类与派生类中拥有同名成员,派生类成员将会屏蔽基类对同名成员的直接访问,这种就叫做重定义(同名隐藏);
可以使用 基类::基类成员 访问,也可以使用 _super::基类成员 访问。
class Base
{
public:
void TestFunc()
{
cout << "Base::TestFunc()" << endl;
}
int _b;
};
class Derived : public Base
{
public:
void TestFunc(int b)//成员函数重定义
{
Base::_b = 100;
__super::_b = 10;
cout << "Derived::TestFunc()" << endl;
}
char _b;//成员变量重定义
};
int main()
{
Derived d;
d.TestFunc(10);
d.Base::TestFunc();
d.Base::_b = 10;
return 0;
}
3. 若要构成重定义,只需要函数名相同即可。
// Base 的 _age 和 Derived 的 _age 构成隐藏关系,这样的代码虽然能运行,但是非常容易混淆
class Base
{
protected:
string _name = zhangsan;
int _age = 20;
};
class Derived:public Base
{
public:
void Print()
{
cout<<"姓名:"<<_name<<endl;
cout<<"年龄:"<<_age<<endl;
}
protected:
int _age = 30;
};
void Test()
{
Derived d;
d.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);
}
4.派生类的默认成员函数
1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员,若基类中没有默认的构造函数,就必须在派生类中的构造函数的初始化列表中显示调用;
class Base
{
public:
Base(int b)
: _b(b)
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
protected:
int _b;
};
class Derived : public Base
{
public:
Derived(int b, int d)
: Base(b) // 初始化基类部分继承下来的成员
, _d(d)
{
cout << "Derived()" << endl;
}
~Derived()
{
cout << "~Derived()" << endl;
// call ~Base();
}
protected:
int _d;
};
void TestFunc()
{
Derived d(1,2);
}
int main()
{
TestFunc();
return 0;
}
若派生类中用户没有显示出构造函数,那么系统会自动生成一份构造函数
//基类的构造函数不是默认构造函数
Derived()
: Base(10)
, _d(0xCCCCCCCC) // 栈上的对象
{
cout << "Derived()" << endl;
}
//基类的构造函数就是默认构造函数
Derived()
: _d(0xCCCCCCCC) // 栈上的对象
{
cout << "Derived()" << endl;
}
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化;
3. 派生类的operator=必须要调用基类的operator=完成基类累的赋值;
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
class Base
{
public:
Base(int b)
: _b(b)
{
cout << "Base(int b)" << endl;
}
Base(const Base& b)
: _b(b._b)
{
cout << "Base(const Base& b)" << endl;
}
Base& operator=(const Base& d)
{
if (this != &d)
{
_b = d._b;
}
cout << "Base& operator=(const Base& d)" << endl;
return *this;
}
~Base()
{
cout << "~Base()" << endl;
}
protected:
int _b;
};
class Derived : public Base
{
public:
Derived(int b, int d)
: Base(b)
, _d(d)
{
cout << "Derived(int b,int d)" << endl;
}
Derived(const Derived& d)
: Base(d)
, _d(d._d)
{
cout << "Derived(const Derived& d)" << endl;
}
Derived& operator=(const Derived& d)
{
if (this != &d)
{
__super::operator=(d);
_d = d._d;
}
cout << "Derived& operator=(const Derived& d)" << endl;
return *this;
}
~Derived()
{
cout << "~Derived()" << endl;
// call ~Base();
}
protected:
int _d;
};
int main()
{
Derived d1(1, 2);
Derived d2(3, 4);
Derived d3(d1);
d1 = d2;
return 0;
}
运行结果:
5.继承与友元
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员,因为友元不是类的成员
6.继承与静态成员
基类定义了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 main()
{
Student s1;
Student s2;
Student s3;
Graduate s4;
cout << " 人数 :" << Person::_count << endl;
Student::_count = 0;
cout << " 人数 :" << Person::_count << endl;
}
运行结果: