c++中的继承
1、继承的概念和定义
2、基类和派生类对象的赋值转换
3、继承中的作用域
4、派生类的默认成员函数
5、继承与友元
6、继承与静态成员
7、复杂的菱形继承
——————————————————————————————
1、继承的概念和定义
继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加功能,这样产生新的类,称派生类。
继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。
以前我们接触的复用都是函数复用,继承是类设计层次的复用。
基类的成员变量的无论是什么,都会继承到派生类中,但是 ,派生类不一定能使用继承的成员变量。
继承关系和访问限定符
继承基类成员访问方式的变化
继承权限–public
基类中public/protected的成员在子类中权限不变
基类中private的成员在子类中不可见(不能用—该成员变量确实已经继承到子类中)
不可见—> 派生类对继承的私有成员不能使用,即使在派生类内部,
继承方式–protected
基类中public的成员在子类中访问权限已经变成protected
基类中protected的成员在子类中访问权限不变
基类中private的成员在子类中不可见(不能用—该成员变量确实已经继承到子类中)
继承方式–private
基类中public的成员在子类中访问权限已经变成private
基类中protected的成员在子类中访问权限已经变成private
基类中private的成员在子类中不可见(不能用—该成员变量确实已经继承到子类中)
class 和 struct的默认继承权限
class默认继承权限—priavet
struct默认继承权限—public
继承权限的验证
#include <iostream>
using namespace std;
#if 0
class Base
{
public:
void SetBase(int pri, int pro, int pub)
{
_pri = pri;
_pro = pro;
_pub = pub;
}
void PrintBase()
{
cout << _pri << endl;
cout << _pro << endl;
cout << _pub << endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derived : public Base
{};
int main()
{
// 可以验证:派生类将基类中的成员变量继承到子类中
cout << sizeof(Derived) << endl; //12 ---> 将基类的成员变量继承到子类中
// 验证:基类中的成员函数是否被子类继承
Derived d;
d.SetBase(10, 20, 30);
d.PrintBase();//
return 0;
}
#endif
#if 0
// public继承方式:
class Base
{
public:
void SetBase(int pri, int pro, int pub)
{
_pri = pri;
_pro = pro;
_pub = pub;
}
void PrintBase()
{
cout << _pri << endl;
cout << _pro << endl;
cout << _pub << endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
// 继承权限--public
// 基类中public/protected的成员在子类中权限不变
// 基类中private的成员在子类中不可见(不能用---该成员变量确实已经继承到子类中)
class Derived : public Base
{
public:
void SetDerived(int priD, int proD, int pubD)
{
_priD = priD;
_proD = proD;
_pubD = pubD;
_pro = 10;
// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
}
// 访问权限:限定该成员变量是否可以直接在类外进行调用
public:
int _pubD;
protected:
int _proD;
private:
int _priD;
};
class D : public Derived
{
public:
void Test()
{
_pro = 10;
}
};
int main()
{
Derived d;
cout << sizeof(d) << endl;
d._pub = 10;
// d._pro = 10;
return 0;
}
#endif
#if 0
class Base
{
public:
void SetBase(int pri, int pro, int pub)
{
_pri = pri;
_pro = pro;
_pub = pub;
}
void PrintBase()
{
cout << _pri << endl;
cout << _pro << endl;
cout << _pub << endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
// 继承方式--protected
// 基类中public的成员在子类中访问权限已经变成protected
// 基类中protected的成员在子类中访问权限不变
// 基类中private的成员在子类中不可见(不能用---该成员变量确实已经继承到子类中)
class Derived : protected Base
{
public:
void SetDerived(int priD, int proD, int pubD)
{
_priD = priD;
_proD = proD;
_pubD = pubD;
_pro = 10;
// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
}
// 访问权限:限定该成员变量是否可以直接在类外进行调用
public:
int _pubD;
protected:
int _proD;
private:
int _priD;
};
class D : public Derived
{
public:
void Test()
{
_pub = 10;
_pro = 20;
}
};
int main()
{
Derived d;
// d._pub = 10;
return 0;
}
#endif
#if 0
class Base
{
public:
void SetBase(int pri, int pro, int pub)
{
_pri = pri;
_pro = pro;
_pub = pub;
}
void PrintBase()
{
cout << _pri << endl;
cout << _pro << endl;
cout << _pub << endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
// 继承方式--private
// 基类中public的成员在子类中访问权限已经变成private
// 基类中protected的成员在子类中访问权限已经变成private
// 基类中private的成员在子类中不可见(不能用---该成员变量确实已经继承到子类中)
class Derived : private Base
{
public:
void SetDerived(int priD, int proD, int pubD)
{
_priD = priD;
_proD = proD;
_pubD = pubD;
_pro = 10;
// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
}
// 访问权限:限定该成员变量是否可以直接在类外进行调用
public:
int _pubD;
protected:
int _proD;
private:
int _priD;
};
class D : public Derived
{
public:
void Test()
{
// _pub = 10;
//_pro = 20;
}
};
int main()
{
Derived d;
d._pub = 10;
return 0;
}
#endif
#if 0
class Base
{
public:
void SetBase(int pri, int pro, int pub)
{
_pri = pri;
_pro = pro;
_pub = pub;
}
void PrintBase()
{
cout << _pri << endl;
cout << _pro << endl;
cout << _pub << endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
// class 和 struct的默认继承权限
// class默认继承权限---priavet
// struct默认继承权限---public
struct Derived : Base
{
public:
void SetDerived(int priD, int proD, int pubD)
{
_priD = priD;
_proD = proD;
_pubD = pubD;
_pro = 10;
// _pri = 20; // 编译报错,在派生类中不能访问基类中私有的成员变量
}
public:
int _pubD;
protected:
int _proD;
private:
int _priD;
};
class D : public Derived
{
public:
void Test()
{
_pub = 10;
}
};
int main()
{
D d;
d._pub = 10;
return 0;
}
#endif
2、基类和派生类对象的赋值转换
赋值兼容规则:
前提---->public继承方式
如果是public继承方式:派生类与基类对象之间是–is-a的关系
is-a: 是一个,可以将一个子类对象看成是一个基类对象
1、所有用到基类对象的位置都可以使用子类对象进行代替
2、一个基类指针可以指向子类对象
一个子类的指针不能直接指向一个基类的对象。
将B类型指针进行强转 B* Bp -----> (A*) Bp (不建议,存在风险)
此时 Bp 就像 A* Ap 一样,访问基类时不会越界访问。
A* Ap 访问派生类因为其本也只会访问与基类相同的部分。
3、 基类引用可以引用派生类对象,反之不行 , 因为 引用就是指针。
3、继承中的作用域
同名隐藏:
基类和派生类中具有相同名称的成员(成员变量 || 成员函数)
如果通过派生类对象直接访问同名成员,优先访问到的是派生类自己的。
基类的同名成员不能直接访问到(派生类将基类中的同名成员隐藏)
class B
{
public:
void SetB(int b)
{
_b = b;
}
void Test(int a) // 基类
{}
// protected:
char _b;
};
// 成员变量:与变量类型是否相同无关
// 成员函数:与成员函数原型是否相同无关
class D : public B
{
public:
void SetD(int b, int d)
{
_b = b;
}
void Test() // 派生类
{}
//protected:
int _b;
};
int main()
{
cout << sizeof(D) << endl;
D d;
d._b = '1';
d.B::_b = '2';
d.Test(10);
d.B::Test(10);
// d.SetD(1); // 报错 , 会先从来自于基类中找到,发现没有; 找来自于基类的必须加上 :: 修饰限定符
return 0;
}
4、派生类的默认成员函数
1 、如果基类的构造函数是无参或者全缺省的构造函数在派生类构造函
数初始化列表的位置调用或不调用基类构造函数都可以。
如果用户没有调用,则编译器会默认调用。
class B
{
public:
B() // 基类构造
{
cout << "B()" << endl;
}
protected:
int _b;
};
class D : public B
{
public:
D() // 派生构造
:B() //基类含有默认的构造函数 编译器默认给出 ,用来初始化继承自基类的成员,
//但必须是此时基类必须是默认的构造函数
,_d(2)
{
cout << "D()" << endl;
}
protected:
int _d;
};
int main()
{
D d;
return 0;
}
2、如果基类的构造函数带有参数的构造函数(非默认构造),用户必
须在派生类构造函数初始化列表的位置显式调用,以完成基类部分成
员的初始化.
class B
{
public:
B(int b)
{
cout << "B()" << endl;
}
protected:
int _b;
};
class D : public B
{
public:
D()
: B(1) // 此时必须显示给出,基类不含默认构造函数
, _d(2)
{
cout << "D()" << endl;
}
protected:
int _d;
};
int main()
{
D d;
return 0;
}
3、如果基类的构造函数带有参数的构造函数,用户必
须在派生类构造函数初始化列表的位置显式调用,以完成基类 部分成
员的初始化。
3、如果基类的构造函数带有参数的构造函数,用户必须在
派生类构造函数初始化列表的位置显式调用,以完成基类 部分成员的初始化
class D : public B
{
public:
D(int b, int d)
: B(b)
, _d(d)
{
cout << "D()" << endl;
}
D(const D& d)
: B(d) // 如何理解将派生类对象d调用基类构造呢? 派生类可以看成基类
, _d(d._d)
{}
D& operator=(const D& d)
{
if (this != &d)
{
//*this = d; 导致死循环
B::operator=(d);
_d = d._d;
}
return *this;
}
protected:
int _d;
};
int main()
{
D d1(1, 2);
D d2(d1);
D d3(3, 4);
d2 = d3; // 赋值,
return 0;
}
/*
如果编译器生成默认的构造函数,则编译器生成的构造函数一定是无参的
Derived()
: Base()
{}
派生类默认构造函数在其初始化列表位置调用基类构造函数时,必须调用基类
无参或者全缺省的构造函数
*/
class Base
{
public:
Base(int b)
: _b(b)
{}
Base(const Base& b)
: _b(b._b)
{}
Base& operator=(const Base& b)
{
if (this != &b)
{
_b = b._b;
}
return *this;
}
void SetBase(int b)
{
_b = b;
}
protected:
int _b;
};
class Derived : public Base
{
public:
Derived(int b, int d)
: Base(b)
, _d(d)
{}
Derived(const Derived& d)
: Base(d)
, _d(d._d)
{}
Derived& operator=(const Derived& d)
{
if (this != &d)
{
// 给基类部分成员赋值
Base::operator=(d);
// 给派生类新增加成员赋值
//_d = d._d;//若只对来源于基类进行赋值,则不对派生类进行操作
}
return *this;
}
void SetDerived(int b, int d)
{
_b = b;
_d = d;
}
protected:
int _d;
};
int main()
{
Base b(10);
Derived d1(10, 20);
Derived d2(d1);
Derived d3(30, 40);
d2 = d3;
return 0;
}
在继承体系中,基类与派生类的构造函数与析构函数的调用次序??
先调用派生类自己的构造函数,再跳转至基类构造函数,
class Base
{
public:
Base(int b)
: _b(b)
{
cout << "Base::Base(int)" << endl;
}
~Base()
{
cout << "Base::~Base()" << endl;
}
protected:
int _b;
};
class Derived : public Base
{
public:
Derived(int b, int d)
: Base(b)
, _d(d)
{
cout << "Derived::Derived(int,int)" << endl;
}
~Derived()
{
cout << "Derived::~Derived()" << endl;
// call Base::~Base();
}
protected:
int _d;
};
// 1. 运行结束打印结果
/*
Base::Base(int)
Derived::Derived(int,int)
Derived::~Derived()
Base::~Base()
*/
// 2. 构造和析构的调用次序
/*
打印次序(执行次序):
先调基类构造--->派生类构造--->派生类析构--->基类析构 (从打印结果来看)
// 有些情况下,千万不能过于相信自己的眼睛
// 打印次序并非调用次序
调用次序:
构造次序:
派生类构造函数()
: 基类构造函数() // 打印Base::Base(int)
{
打印Derived(int,int) //派生类构
}
析构次序:
派生类析构函数()
{
打印~Derived()// 释放派生类资源
// 编译器在派生类析构函数最后一条有效语句后插了一条汇编代码
call 基类析构函数; // 打印Base::~Base()
}
*/
void TestDerived()
{
Derived d(10, 20);
}
int main()
{
TestDerived();
return 0;
}
5、继承与友元
友元不能被继承
class Base
{
friend void Print();
public:
Base(int b)
: _b(b)
{}
int GetB()
{
return _b;
}
protected:
int _b;
};
class Derived : public Base
{
public:
Derived(int b, int d)
: Base(b)
, _d(d)
{}
protected:
int _d;
};
void Print()
{
Base b(10);
cout << b.GetB() << endl;
cout << b._b << endl;
Derived d(10, 20);
cout << d._d << endl;
}
6、继承与静态成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成
员。无论派生出多少个子类,都只有一 个static成员实例 。
7、复杂的菱形继承
cout<<"下回讲解"<<endl;