一、继承和派生
类有一种继承的方式,很有趣。一般继承是公有继承。
继承的方式有三种:公有继承(public),保护继承(protected),私有继承(private)。
. | public | protected | private |
---|---|---|---|
公有继承 | public | protected | 不可见 |
保护继承 | protected | protected | 不可见 |
私有继承 | private | private | 不可见 |
public:类的内部可以访问。类的外部可以访问
protected:类的内部可以访问。类的外部不能访问
private:类的内部可以访问。类的外部不能访问
如何恰当的使用public,protected和private为成员声明访问级别?
1、需要被外界访问的成员直接设置为public
2、只能在当前类中访问的成员设置为private
3、只能在当前类和子类中访问的成员设置为protected,protected成
员的访问权限介于public和private之间。
子类与父类之间的关系:
1.子类对象可以当作父类对象使用。
2.子类对象可以直接赋值给父类对象。
3.子类对象可以直接初始化父类对象.
4.父类指针可以直接指向子类对象
5.父类引用可以直接引用子类对象
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Parent
{
public:
void printP() {
cout << "a " << this->a << endl;
}
int a;
};
class Child :public Parent
{
public:
void printC()
{
cout << "b = " << this->b << endl;
}
int b;
};
void myPrint(Parent *pp)
{
pp->printP();
}
int main(void)
{
Parent p;
//Child c = p; //p对象填充不满c对象空间,
Child c;
Parent p = c;//c 对象所占用的内存空间 >= p对象占用空间 能够填充满p对象所需要空间。
p = c;
// c.printP(); //c 能够当做父类 p 来使用。
Parent *pp = NULL;//父类指针
Child *cp = NULL;//子类指针
Parent p;//父类对象
Child c; //子类对象
pp = &c;//c 内存布局能够满足父类指针的全部需求, 可以用一个儿子的对象地址给父类指针赋值。
myPrint(&p);
myPrint(&c);
return 0;
}
二、子类与父类之间的构造函数与析构函数
1.在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初
始化.
2.在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清
理.
继承和组合并存,构造和析构原则是声明呢?
答案是:
先构造父类,再构造成员变量、最后构造自己
先析构自己,在析构成员变量、最后析构父类
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Parent
{
public:
Parent()
{
cout << "Parent().." << endl;
a = 0;
}
Parent(int a) {
cout << "Parent(int)..." << endl;
this->a = a;
}
~Parent() {
cout << "~Parent" << endl;
}
int a;
};
class Child :public Parent
{
public:
//在调用子类的构造函数时候,一定会调用父类的构造函数
// 父类先构造,子类后构造。
Child(int a, int b) :Parent(a)
{
cout << "Child(int, int)..." << endl;
this->b = b;
}
void printC() {
cout << "b = " << b << endl;
}
~Child() {
cout << "~Child()..." << endl;
}
int b;
};
int main(void)
{
Child c(10, 20);
c.printC();
return 0;
}
运行结果如下:
三、子类与父类成员重名
如果是父类中有一个成员,与子类中的变量重名,要怎么解决呢,看以下代码:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Parent
{
public:
Parent(int a) {
this->a = a;
}
int a;
};
class Child :public Parent
{
public:
Child(int p_a, int c_a) :Parent(p_a)
{
this->a = c_a;
}
void print()
{
cout << Parent::a << endl;//在子类中使用父类的a,需要添加作用域。
cout << this->a << endl;//child's a
}
int a;
};
int main(void)
{
Child c(10, 100);
c.print();
return 0;
}
运行结果如下:
四、多继承
一个类同时继承两个类。
五、虚继承 virtual
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则
在对该基类中声明的名字进行访问时,可能产生二义性
虚继承声明使用关键字 virtual
虚继承为了防止这种情况下,类C,继承类B的成员后,不清晰是从类B1中继承下来的,还是类B2中继承下来的。
下面看一个例子。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//家具类
class Furniture
{
public:
int m; //材质
};
//将父亲类继承爷爷类 改成虚继承, 防止儿子在多继承我的时候,出现爷爷中的变量会拷贝多份。
class Bed :virtual public Furniture
{
public:
void sleep() {
cout << "在床上睡觉" << endl;
}
};
class Sofa :virtual public Furniture
{
public:
void sit() {
cout << "在沙发上休息" << endl;
}
};
//沙发床
class SofaBed :public Bed, public Sofa
{
public:
void SleepAndSit() {
sleep();
sit();
}
};
int main(void)
{
Bed b;
b.sleep();
Sofa s;
s.sit();
cout << " ------ " << endl;
SofaBed sb;
sb.SleepAndSit();
sb.m = 100;//此时只有一个m
//sb.Bed::m = 100;
//sb.Sofa::m = 200;
return 0;
}
运行结果如下:
六、多态
多态发生的条件:
1.要有继承
2.要有虚函数重写
3.要有父类指针(父类引用)指向子类对象
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
//岳不群
class Yuebuqun
{
public:
Yuebuqun( string kongfu)
{
this->kongfu = kongfu;
}
virtual void fight() //标识修饰一个成员方法是一个虚函数。
{
cout << "岳不群" << "使出了" << kongfu << "打人" << endl;
}
void print()
{
}
string kongfu;
};
//林平之 继承了 岳不群
class Linpingzhi :public Yuebuqun
{
public:
Linpingzhi(string kongfu) :Yuebuqun(kongfu)
{
}
//如果说父类中有一个虚函数是fight( ), 子类如果去重写这个虚函数。
void fight()
{
cout << "林平之" << "使出了" << kongfu << "打人" << endl;
}
void print()
{
}
};
class Linghuchong :public Yuebuqun
{
public:
Linghuchong(string kongfu) :Yuebuqun(kongfu)
{
}
void fight()
{
cout << "令狐冲 " << "使用了" << kongfu << endl;
}
};
//在全局提供一个打斗的方法
void fightPeople(Yuebuqun *hero)//Yuebuqun *hero = xiaopp; Yuebuqun *hero = xiaoyy;
{
cout << "调用打人的方法" << endl;
hero->fight();//希望传递进来的如果是子类,调用子类的fight
//如果传递进来的是父类, 调用父类的fight
//这种行为就是 多态行为。
}
//多态发生的三个必要条件:
//1. 要有继承。
//2. 要有虚函数重写。
//3. 父类指针或引用 指向 子类对象。
int main(void)
{
Yuebuqun *xiaoyy = new Yuebuqun("葵花宝典");
//xiaoyy->fight();
Linpingzhi *xiaopp = new Linpingzhi("僻邪剑谱");
//xiaopp->fight();
Linghuchong *xiaoll = new Linghuchong("独孤九剑");
fightPeople(xiaoyy);
fightPeople(xiaopp);
fightPeople(xiaoll);
//编译器默认做了一个安全的处理。 编译认为 不管传递时子类对象还是父类对象,
//如果统一执行父类d方法 那么是一定可以被成功执行。
delete xiaoyy;
delete xiaopp;
delete xiaoll;
return 0;
}
七、虚析构函数
如果父类在堆上开辟空间。子类也在堆上开辟空间。
进行析构的时候,会先构造父类,在构造子类。再进行析构时,如果不是虚构造函数,子类无法析构掉,导致内存泄露。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class A
{
public:
A()
{
p = new char[20];
strcpy(p,"obja");
printf("A()\n");
}
virtual ~A()
{
delete[] p;
printf("~A()\n");
}
private:
char *p;
};
class B : public A
{
public:
B()
{
p = new char[20];
strcpy(p, "objb");
printf("B()\n");
}
~B()
{
delete[] p;
printf("~B()\n");
}
private:
char *p;
};
class C : public B
{
public:
C()
{
p = new char[20];
strcpy(p, "objc");
printf("C()\n");
}
~C()
{
delete[] p;
printf("~C()\n");
}
private:
char *p;
};
//通过⽗类指针 把 所有的⼦类对象的析构函数 都执⾏⼀遍
//通过⽗类指针 释放所有的⼦类资源
void howtodelete(A *base)
{
delete base;
}
int main()
{
C *myC = new C;
//delete myC; //直接通过⼦类对象释放资源 不需要写virtual
howtodelete(myC);//通过⽗类的指针调⽤释放⼦类的资源
return 0;
}
运行结果如下:
八、重载,重写,重定义
1.重载:一定是发生在同一个作用域下。
2.重定义:是发生在两个不同的类中,一个父类,一个子类。
(1)普通函数重定义
(2)虚函数重写