代码重用
- C++很重要的一个特征就是代码重用。在c语言中重用代码的方法就是拷贝代码、修改代码。C++可以继承或组合的方式来重用。通过组合或继承现有的类来创建新类,而不是重新创建它们。
继承
- 继承是使用已经编写好的类来创建新类,新的类具有原有类的所有属性和操作,也可以在原有类的基础上做一些修改和增补。
- 新类称为派生类或子类,原有类称为基类或父类
- 派生类是基类的具体化。
- 派生类的声明语法为:
class 派生类名:继承方式 基类名
{
派生类新增成员的声明;
}
#include <iostream>
using namespace std;
class Base
{
public:
int x_;
protected:
int y_;
private:
int z_;
};
class PublicInherit:public Base
{
public:
void Test()
{
x_= 10;
y_= 20;
//z_= 30; // 派生类不能访问基类的私有成员
}
private:
int a_;
};
int main()
{
Base b;
b.x_ = 20;
//b.y_ = 30;//对象不能访问保护成员
return 0;
}
公有、私有、保护继承
- 在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数
- 在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
- 在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同。
默认继承保护级别
- class Base {};
- struct D1 : Base {}; //公有继承
- class D2 : Base {}; //私有继承
接口继承与实现继承
- 我们将类的公有成员函数称为接口。
- 公有继承,基类的公有成员函数在派生类中仍然是公有的,换句话说是基类的接口成为了派生类的接口,因而将它称为接口继承。
- 实现继承,对于私有、保护继承,派生类不继承基类的接口。派生类将不再支持基类的公有接口,它希望能重用基类的实现而已,因而将它称为实现继承。
继承与重定义
- 对基类的数据成员重定义
- 对基类成员函数的重定义分为两种
- overwrite
- 与基类完全相同
- 与基类成员函数名相同,参数不同
- override
overload是发生在作用域相同的时候
override是覆盖
- overwrite
#include <iostream>
using namespace std;
class Base
{
public:
Base() : x_(0)
{
}
int GetBaseX() const
{
return x_;
}
void Show()
{
cout << "Base::Show..."<<endl;
}
int x_;
};
class Derived : public Base
{
public:
Derived() : x_(0)
{
}
int GetDerivedX() const
{
return x_;
}
void Show(int n)
{
cout << "Derived::Show" <<n <<endl;
}
void Show()
{
cout << "Derived::Show" <<endl;
}
int x_;
};
int main()
{
Derived d;
d.x_ = 10;
d.Base::x_ = 20;
cout << d.GetBaseX() <<endl;
cout << d.GetDerivedX() << endl;
d.Show();
d.Base::Show();
}
继承与组合
- 无论是继承与组合本质上都是把子对象放在新类型中,两者都是使用构造函数的初始化列表去构造这些子对象。
- 组合通中是在希望新类内部具有已存在的类的功能时使用,而不是希望已存在类作为它的接口。组合通过嵌入一个对象以实现新类的功能,而新类用户看到的是新定义的接口,而不是来自老类的接口。(has-a)
- 如果希望新类与已存在的类有相同的接口(在这基础上可以增加自己的成员)。这时候需要用继承,也称为子类型化。(is-a)
里氏代换原则 检验继承的质量
不能自动继承的成员函数
- 构造函数
- 析构函数
- =运算符
继承与构造函数
- 基类的构造函数不被继承,派生类中需要声明自己的构造函数
- 声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化(调用基类构造函数完成)
- 派生类的构造函数需要给基类的构造函数传递参数
#include <iostream>
using namespace std;
class ObjectB
{
public:
ObjectB(int objb):objb_(objb)
{
cout<<"ObjectB...."<<endl;
}
~ObjectB()
{
cout<<"~ObjectB...."<<endl;
}
int objb_;
};
class ObjectD
{
public:
ObjectD(int objd):objd_(objd)
{
cout<<"ObjectD...."<<endl;
}
~ObjectD()
{
cout<<"~ObjectD...."<<endl;
}
int objd_;
};
class Base
{
public:
Base(int b):b_(b),objb_(111)
{
cout << "Base..."<<endl;
}
Base(const Base& other) : objb_(other.objb_),b_(other.b_)
{
}
~Base()
{
cout << "~Base..."<<endl;
}
int b_;
ObjectB objb_;
};
class Derived:public Base
{
public:
Derived(int b,int d):d_(d) ,Base(b),objd_(1111)
{
cout<<"Derived..."<<endl;
}
Derived(const Derived& other):d_(other.d_),objd_(other.objd_),Base(other)
{
}
~Derived()
{
cout<<"~Derived..."<<endl;
}
int d_;
ObjectD objd_;
};
int main()
{
Derived d(100,200);
cout <<d.b_<<" "<<d.d_ <<" "<<endl;
Base b1(100);
Base b2(b1);
cout << b2.b_<<endl;
Derived d2(d);
return 0;
}
类的对象成员没有默认构造函数的时候,只能够有在类的构造函数初始化列表中调用该对象的构造函数进行初始化
基类没有默认构造函数的时候,基类的构造函数要在派生类构造函数初始化列表中调用
派生类对象的构造次序
先调用基类的对象成员的构造函数,然后构造基类构造函数、然后是派生类的对象成员的构造函数,最后是派生类自身的构造函数
友元关系与继承
- 友元关系不能被继承
A是B的友元类,C是A的派生类,那么C不是B的友元类
友元关系是单向的
友元关系是不能被传递的
A是B的友元类,B是C的友元类,那么A不是C的友元类
静态成员与继承
- 静态成员无所谓继承
#include "iostream"
using namespace std;
class Base
{
public:
static int b_;
};
int Base::b_ = 100;
class Derived:public Base
{
};
int main()
{
Base b;
Derived d;
cout << Base::b_<<endl;
cout << b.b_<<endl;
cout << Derived::b_<<endl;
cout << d.b_<<endl;
return 0;
}