C++深度解析教程笔记13
第49课 - 多态的概念和意义
函数重写回顾
■父类中被重写的函数依然会继承给子类·
■子类中重写的函数将覆盖父类中的函数
■通过作用域分辩辨符(::)可以访问到父类中的函数
Child c;
Farent* p = &c;
c.Parent::print();//从父类中继承
c.print(); //在子类中重写
P->print();//父类中定义
面向对象中期望的行为
一根据实际的对象类型判断如何调用重写函数
一父类指针(引用)指向
·交类对象则调用交类中定义的函数
·子类对象则调用子类中定义的重写函数
C++语言直接支持多态的概念
一通过使用virtual关键字对多态进行支持
一被virtual声明的函数被重写后具有多态特性
一被virtual声明的函数叫做虚函数
实验-virtual的使用
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
virtual void print()//virtual virtual void print()
{
cout << "I'm Parent." << endl;
}
};
class Child : public Parent
{
public:
void print()//也是虚函数 virtual 可加,可不加
{
cout << "I'm Child." << endl;
}
};
void how_to_print(Parent* p)//父类指针
{
p->print(); // 展现多态的行为
}
int main()
{
Parent p;
Child c;
how_to_print(&p); // Expected to print: I'm Parent.
how_to_print(&c); // Expected to print: I'm Child.
// I'm Parent.
//I'm Child.
return 0;
}
多态的意义
一在程序运行过程中展现出动态的特性
一函数重写必须多态实现,否则没有意义
一多态是面向对象组件化程序设计的基础特性
实验-重载与多态
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
virtual void func()
{
cout << "void func()" << endl;
}
virtual void func(int i)
{
cout << "void func(int i) : " << i << endl;
}
virtual void func(int i, int j)
{
cout << "void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
}
};
class Child : public Parent
{
public:
void func(int i, int j)
{
cout << "void func(int i, int j) : " << i + j << endl;
}
void func(int i, int j, int k)
{
cout << "void func(int i, int j, int k) : " << i + j + k << endl;
}
};
void run(Parent* p)
{
p->func(1, 2); // 展现多态的特性
// 动态联编
}
int main()
{
Parent p;
p.func(); // 静态联编 void func()
p.func(1); // 静态联编void func(int i) :1
p.func(1, 2); // 静态联编void func(int i, int j) :(1, 2)
cout << endl;
Child c;
c.func(1, 2); // 静态联编void func(int i, int j) :3
cout << endl;
run(&p);//void func(int i, int j) :(1, 2)
run(&c);//void func(int i, int j) :3
return 0;
}
实验-多态的例子
#include <iostream>
#include <string>
using namespace std;
class Boss
{
public:
int fight()
{
int ret = 10;
cout << "Boss::fight() : " << ret << endl;
return ret;
}
};
class Master
{
public:
virtual int eightSwordKill()//虚函数 virtual
{
int ret = 8;
cout << "Master::eightSwordKill() : " << ret << endl;
return ret;
}
};
class NewMaster : public Master//继承了
{
public:
int eightSwordKill()
{
int ret = Master::eightSwordKill() * 2;
cout << "NewMaster::eightSwordKill() : " << ret << endl;
return ret;
}
};
void field_pk(Master* master, Boss* boss)
{
int k = master->eightSwordKill();
int b = boss->fight();
if( k < b )
{
cout << "Master is killed..." << endl;
}
else
{
cout << "Boss is killed..." << endl;
}
}
int main()
{
Master master;
Boss boss;
cout << "Master vs Boss" << endl;
field_pk(&master, &boss);
//Master::eightSwordKill() : 8
//Boss::fight() : 10
//Master is killed...
cout << "NewMaster vs Boss" << endl;
NewMaster newMaster;
field_pk(&newMaster, &boss);// Master* 类型 ,要用虚函数,否则依然是8
//NewMaster::eightSwordKill() : 16
//Boss::fight() : 10
//Boss is killed...
return 0;
}
小结
函数重写只可能发生在交类与子类之间
根据实际对象的类型确定调用的具体函数
virtulal关键字是C++中支持多态的唯一方式
被重写的虚函数可表现出多态的特性
第50课 - C++ 对象模型分析(上)
class是一种特殊的struct
一在内存中class依旧可以看作变量的集合
一class与struct遵循相同的内存对齐规则
一class中的成员函数与成员变量是分开存放的
·每个对象有独立的成员变量
·所有对象共享类中的成员函数
实验??(多了字节数)
#include <iostream>
#include <string>
using namespace std;
class A
{
int i;
int j;
char c;
double d;
public:
void print()
{
cout << "i = " << i << ", "
<< "j = " << j << ", "
<< "c = " << c << ", "
<< "d = " << d << endl;
}
};
struct B
{
int i;
int j;
char c;
double d;
};
int main()
{
A a;
cout << "sizeof(A) = " << sizeof(A) << endl; // 20 bytes
cout << "sizeof(a) = " << sizeof(a) << endl;
cout << "sizeof(B) = " << sizeof(B) << endl; // 20 bytes
cout << "sizeof(double) = " << sizeof(double) << endl;
a.print();
B* p = reinterpret_cast<B*>(&a);//强制类型转换
//reinterpret_cast 强制类型转换
//用于指针类型间的强制转换
//用于整数和指针类型间的强制转换
p->i = 1;
p->j = 2;
p->c = 'c';
p->d = 3;
a.print();
p->i = 100;
p->j = 200;
p->c = 'C';
p->d = 3.14;
a.print();
return 0;
}
//sizeof(A) = 24 ???
//sizeof(a) = 24
//sizeof(B) = 24
//i = 8, j = 0, c = , d = 7.29739e-317
//i = 1, j = 2, c = c, d = 3
//i = 100, j = 200, c = C, d = 3.14
C++对象模型分析
■运行时的对象退化为结构体的形式
一所有成员变量在内存中依次排布
一成员变量间可能存在内存空
一可以通过内存地址直接访问成员变量
一访问权限关键学在运行时失效
类中的成员函数位于代码段中
■调用成员函数时对象地址作为参数隐式传递
■成员函数通过对象地址访问成员变量
■C++语法规则隐藏了对象地址的传递过程
实验-对象模型
#include <iostream>
#include <string>
using namespace std;
class Demo
{
int mi;
int mj;
public:
Demo(int i, int j)
{
mi = i;
mj = j;
}
int getI()
{
return mi;
}
int getJ()
{
return mj;
}
int add(int value)
{
return mi + mj + value;
}
};
int main()
{
Demo d(1, 2);
cout << "sizeof(d) = " << sizeof(d) << endl;//8
cout << "d.getI() = " << d.getI() << endl;//1
cout << "d.getJ() = " << d.getJ() << endl;//2
cout << "d.add(3) = " << d.add(3) << endl;//6
return 0;
}
实验-C语言实现面向对象
//50-2.h
#ifndef _50_2_H_
#define _50_2_H_
typedef void Demo;
Demo* Demo_Create(int i, int j);
int Demo_GetI(Demo* pThis);
int Demo_GetJ(Demo* pThis);
int Demo_Add(Demo* pThis, int value);
void Demo_Free(Demo* pThis);
#endif
//50-2.c
#include "50-2.h"
#include "malloc.h"
struct ClassDemo
{
int mi;
int mj;
};
Demo* Demo_Create(int i, int j)
{
struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));
if( ret != NULL )
{
ret->mi = i;
ret->mj = j;
}
return ret;
}
int Demo_GetI(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mi;
}
int Demo_GetJ(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mj;
}
int Demo_Add(Demo* pThis, int value)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mi + obj->mj + value;
}
void Demo_Free(Demo* pThis)
{
free(pThis);
}
//main.c
#include <stdio.h>
#include "50-2.h"
int main()
{
Demo* d = Demo_Create(1, 2); // Demo* d = new Demo(1, 2);
printf("d.mi = %d\n", Demo_GetI(d)); // d->getI();
printf("d.mj = %d\n", Demo_GetJ(d)); // d->getJ();
printf("Add(3) = %d\n", Demo_Add(d, 3)); // d->add(3);
// d->mi = 100;报错
Demo_Free(d);
return 0;
}
/*
output:
d.mi = 1
d.mj = 2
Add(3) = 6
*/
小结
C++ 中的类对象在内存布局上与结构体相同
成员变量和成员函数在内存中分开存放
访问权限关键学在运行时失效
调用成员函数时对象地址作为参数隐式传递
第51课 - C++ 对象模型分析(下)
继承对象模型
实验-对象指针转换
#include <iostream>
#include <string>
using namespace std;
class Demo
{
protected:
int mi;
int mj;
};
class Derived : public Demo
{
int mk;
public:
Derived(int i, int j, int k)
{
mi = i;
mj = j;
mk = k;
}
void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << ", "
<< "mk = " << mk << endl;
}
};
struct Test//public
{
int mi;
int mj;
int mk;
};
int main()
{
cout << "sizeof(Demo) = " << sizeof(Demo) << endl; //8
cout << "sizeof(Derived) = " << sizeof(Derived) << endl; //12
Derived d(1, 2, 3);
Test* p = reinterpret_cast<Test*>(&d);
//reinterpret_cast 强制类型转换
//用于指针类型间的强制转换
//用于整数和指针类型间的强制转换
cout << "Before changing ..." << endl;
//继承的对象与普通对象
d.print();
p->mi = 10;
p->mj = 20;
p->mk = 30;
cout << "After changing ..." << endl;
d.print();
return 0;
}
/*
sizeof(Demo) = 8
sizeof(Derived) = 12
Before changing ...
mi = 1, mj = 2, mk = 3
After changing ...
mi = 10, mj = 20, mk = 30
*/
多态对象模型
多态对象模型
■C++多态的实现原理
一当类中声明虚函数时,编译器会在类中生成一个虚函数表
一虚函数表是一个存储成员函数地址的数据结构
一虚函数表是由编译器自动生成与维护的
一virtual成员函数会被编译器放入虚函数表中
一存在虚函数时,每个对象中都有一个指向虚函数表的指针
实验-多态对象模型
#include <iostream>
#include <string>
using namespace std;
class Demo
{
protected:
int mi;
int mj;
public:
virtual void print()//virtual 虚函数表指针占用8字节空间
{
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << endl;
}
};
class Derived : public Demo
{
int mk;
public:
Derived(int i, int j, int k)
{
mi = i;
mj = j;
mk = k;
}
void print()
{
cout << "mi = " << mi << ", "
<< "mj = " << mj << ", "
<< "mk = " << mk << endl;
}
};
struct Test//public
{
void* p;//虚函数表指针
int mi;
int mj;
int mk;
};
int main()
{
cout << "sizeof(Demo) = " << sizeof(Demo) << endl; //8
cout << "sizeof(Derived) = " << sizeof(Derived) << endl; //12
Derived d(1, 2, 3);
Test* p = reinterpret_cast<Test*>(&d);
//reinterpret_cast 强制类型转换
//用于指针类型间的强制转换
//用于整数和指针类型间的强制转换
cout << "Before changing ..." << endl;
d.print();
//虚函数表的指针修改值
p->mi = 10;
p->mj = 20;
p->mk = 30;
cout << "After changing ..." << endl;
d.print();
return 0;
}
实验-C语言实现多态-要梳理一下??
#ifndef _51_2_H_
#define _51_2_H_
typedef void Demo;//父类
typedef void Derived;//子类
Demo* Demo_Create(int i, int j);
int Demo_GetI(Demo* pThis);
int Demo_GetJ(Demo* pThis);
int Demo_Add(Demo* pThis, int value);
void Demo_Free(Demo* pThis);
//子类的3个函数
Demo* Derived_Create(int i, int j,int k);
int Derived_GetK(Derived* pThis);
int Derived_Add(Derived* pThis, int value);//虚函数
#endif
#include "51-2.h"
#include "malloc.h"
//6真正的虚函数
static int Demo_Virtual_Add(Demo* pThis, int value);
static int Derived_Virtual_Add(Demo* pThis, int value);//12
struct vtable//1.5 建立虚函数表类
{
int (*pAdd)(Derived*, int);//2放入函数指针 ,要实现add函数的多态
};
struct ClassDemo
{
struct vtable* vptr;//1虚函数表指针,考虑指针的类型
int mi;
int mj;
};
struct ClassDerived
{
struct ClassDemo d;//实现继承
int mk;
};
static struct vtable g_demo_vtbl={
Demo_Virtual_Add
};//3建立虚函数变量 5.static隐藏此变量,外部文件不可访问
//9.真正的虚函数初始化pAdd指针
static struct vtable g_derived_vtbl={
Derived_Virtual_Add
};//11.子类部分
Demo* Demo_Create(int i, int j)
{
struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));
if( ret != NULL )
{
ret->vptr=&g_demo_vtbl;//4 指针指向虚函数表 关联对象和虚函数表
ret->mi = i;
ret->mj = j;
}
return ret;
}
int Demo_GetI(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mi;
}
int Demo_GetJ(Demo* pThis)
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mj;
}
//7 虚函数表指针指向的虚函数
static int Demo_Virtual_Add(Demo* pThis, int value)//case1
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
return obj->mi + obj->mj + value;
}
//case1 case2函数重复吗? 2是对外的用户使用的
//6分析具体的虚函数
int Demo_Add(Demo* pThis, int value)//case2
{
struct ClassDemo* obj = (struct ClassDemo*)pThis;
//return obj->mi + obj->mj + value;//8修改
return obj->vptr->pAdd(pThis,value);
}
void Demo_Free(Demo* pThis)
{
free(pThis);
}
Demo* Derived_Create(int i, int j,int k)
{
struct ClassDerived* ret = (struct ClassDerived*)malloc(sizeof(struct ClassDerived));
if( ret != NULL )
{
ret->d.vptr=&g_derived_vtbl; //10.子类部分
ret->d.mi = i;//子类赋值
ret->d.mj = j;//子类赋值
ret->mk=k;
}
return ret;
}
int Derived_GetK(Derived* pThis)
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;//强制类型转换
return obj->mk;
}
static int Derived_Virtual_Add(Demo* pThis, int value)//13
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;
return obj->mk + value;
}
int Derived_Add(Derived* pThis, int value)//虚函数
{
struct ClassDerived* obj = (struct ClassDerived*)pThis;
return obj->d.vptr->pAdd(pThis,value);//14.
// return obj->mk+obj->d.mi +obj->d.mj+ value;
}
//main.c
#include "51-2.h"
#include <stdio.h>
void run(Demo* p,int v)
{
int r=Demo_Add(p,v);
printf("r=%d\n",r);
}
//C实现多态,面向对象
int main()
{
Demo* p1=Demo_Create(1,2);
Derived* c1=Derived_Create(11,22,100);
printf("Demo_Add(p,3)=%d\n",Demo_Add(p1,3));//6
printf("Derived_Add(c,3)=%d\n",Derived_Add(c1,3));//103
run(p1,3);//6
run(c1,3);//36 11+22+3 并不是期望的103,没有调用Derived_Add,而是调用Demo_Add
//实现多态
//6
//103
Demo_Free(p1);//C语言没有析构函数 ,手动释放内存
Demo_Free(c1);
return 0;
}
小结
继承的本质就是父子间成员变量的叠加
C++中的多态是通过虚函数表实现的
壶函数表是由编译器自动生成与维护的
虚函数的调用效率低于普通成员函数
第52课 - C++ 中的抽象类和接口
在现实中需要知道具体的图形类型才能求面积所以对概念上的“图形”求面积是没有意义的!
class shape
{
public:
double area(){
return 0;}
};
Shape只是一个概念上的类型,没有具体对象!
什么是抽象类?
■面向对象中的抽象类
一可用于表示现实世界中的抽象概念
一是一种只能定义类型,而不能产生对象的类
一只能被继承并重写相关函数
一直接特征是相关函数没有完整的实现
纯虚函数的语法规则
class shape{
public:
virtual double area() = 0;
};
"=0"用于告诉编译器当前是声明纯虚函数,因此不需要定义函数体。
C++语言中没有抽象类的概念
C++中通过纯虚函数实现抽象类
纯虚函数是指只定义原型的成员函数
一个C++类中存在纯虚函数就成为了抽象类
Shape是现实世界中各种图形的抽象概念因此:
一程序中必须能够反映抽象的图形
一程序中通过抽象类表示图形的概念
一抽象类不能创建对象,只能用于继承
实验-纯虚函数
#include <iostream>
#include <string>
using namespace std;
class Shape
{
public:
virtual double area() = 0;//纯虚函数 告诉编译器当前是声明纯虚函数,因此不需要定义函数体。
};
class Rect : public Shape//继承
{
int ma;
int mb;
public:
Rect(int a, int b)
{
ma = a;
mb = b;
}
double area()
{
return ma * mb;
}
};
class Circle : public Shape
{
int mr;
public:
Circle(int r)
{
mr = r;
}
double area()
{
return 3.14 * mr * mr;
}
};
void area(Shape* p)//父类指针
{
double r = p->area();
cout << "r = " << r << endl;
}
int main()
{
Rect rect(1, 2);
Circle circle(10);
area(&rect);
area(&circle);
//r = 2
//r = 314
return 0;
}
满足下面条件的C++类则称为接口
一类中没有定义任何的成员变量
一所有的成员函数都是公有的
一所有的成员函数都是纯虚函数
一接口是一种特殊的抽象类
抽象类只能用作父类被继承
子类必须实现纯虚函数的具体功能
纯虚函数被实现后成为虚函数
如果子类没有实现纯虚函数,则子类成为抽象类
实验-接口
#include <iostream>
#include <string>
using namespace std;
class Channel
{
public:
virtual bool open() = 0;
virtual void close() = 0;
virtual bool send(char* buf, int len) = 0;
virtual int receive(char* buf, int len) = 0;
};
int main()
{
return 0;
}
小结
抽象类用于描述现实世界中的抽象概念
抽象类只能被继承不能创建对象
C++中没有抽象类的概念
C++中通过纯虚函数实现抽象类
类中只存在纯虚函数的时成为接口
接口是一种特殊的抽象类
第53课 - 被遗弃的多重继承(上)
C++中是否允许一个类继承自多个父类?
多重继承的语法规则
class Derived : public BaseA,public Base, public Basec
{
//....
};
多重继承的本质与单继承相同!
C++支持编写多重继承的代码
一一个子类可以拥有多个交类
一子类拥有所有父类的成员变量
一子类继承所有交类的成员函数
一子类对象可以当作任意父类对象使用
实验-多个父类指针的指向位置
#include <iostream>
#include <string>
using namespace std;
class BaseA//父类
{
int ma;
public:
BaseA(int a)
{
ma = a;
}
int getA()
{
return ma;
}
};
class BaseB//父类
{
int mb;
public:
BaseB(int b)
{
mb = b;
}
int getB()
{
return mb;
}
};
class Derived : public BaseA, public BaseB//多重继承
{
int mc;
public:
Derived(int a, int b, int c) : BaseA(a), BaseB(b)
{
mc = c;
}
int getC()
{
return mc;
}
void print()
{
cout << "ma = " << getA() << ", "
<< "mb = " << getB() << ", "
<< "mc = " << mc << endl;
}
};
int main()
{
cout << "sizeof(Derived) = " << sizeof(Derived) << endl; // 12
Derived d(1, 2, 3);
d.print();
cout << "d.getA() = " << d.getA() << endl;
cout << "d.getB() = " << d.getB() << endl;
cout << "d.getC() = " << d.getC() << endl;
cout << endl;
BaseA* pa = &d;//父类指针指向子类对象
BaseB* pb = &d;//父类指针指向子类对象
cout << "pa->getA() = " << pa->getA() << endl;
cout << "pb->getB() = " << pb->getB() << endl;
cout << endl;
void* paa = pa;
void* pbb = pb;
if( paa == pbb )//虽然是同一个对象,指向的对象内部的不同位置
{
cout << "Pointer to the same object!" << endl;
}
else
{
cout << "Error" << endl;
}
cout << "pa = " << pa << endl;
cout << "pb = " << pb << endl;
cout << "paa = " << paa << endl;
cout << "pbb = " << pbb << endl;
return 0;
}
/*
sizeof(Derived) = 12
ma = 1, mb = 2, mc = 3
d.getA() = 1
d.getB() = 2
d.getC() = 3
pa->getA() = 1
pb->getB() = 2
Error
pa = 0x61fde4
pb = 0x61fde8
paa = 0x61fde4
pbb = 0x61fde8
*/
实验-多重继承带来的冗余
#include <iostream>
#include <string>
using namespace std;
class People//父类
{
string m_name;
int m_age;
public:
People(string name, int age)
{
m_name = name;
m_age = age;
}
void print()
{
cout << "Name = " << m_name << ", "
<< "Age = " << m_age << endl;
}
};
class Teacher :virtual public People//virtual public People public People case2
{
public:
Teacher(string name, int age) : People(name, age)
{
}
};
class Student : virtual public People//virtual public People public People case2
{
public:
Student(string name, int age) : People(name, age)
{
}
};
class Doctor : public Teacher, public Student//多重继承
{
public:
Doctor(string name, int age) : Teacher(name, age), Student(name, age), People(name, age)//Teacher(name, age), Student(name, age), People(name, age)
{
}
//Doctor(string name, int age) : Teacher(name+"1", age+1), Student(name+"2", age+2)() case2
};
int main()
{
Doctor d("Delphi", 33);
//d.print();//error: request for member 'print' is ambiguous
d.Teacher::print();
d.Student::print();
//case2
//Name = Delphi1, Age = 34
//Name = Delphi2, Age = 35
//case3:虚继承 增加复杂性
d.print();
return 0;
}
虚继承能够解决数据瓦余问题
中间层父类不再关心顶层交类的初始化
最终子类必须直接调用顶层父类的构造函数
小结
C++支持多重继承的编程方式
多重继承容易带来问题
一可能出现“同一入对象的地址不同”的情况
一虚继承可以解决数据亢余的问题
一虚继承的使得架构设计可能出现问题
第54课 - 被遗弃的多重继承(下)
实验-多重继承产生多个虚函数表,解决方法dynamic cast
#include <iostream>
#include <string>
using namespace std;
class BaseA
{
public:
virtual void funcA()
{
cout << "BaseA::funcA()" << endl;
}
};
class BaseB
{
public:
virtual void funcB()
{
cout << "BaseB::funcB()" << endl;
}
};
class Derived : public BaseA, public BaseB //多重继承
{
};
int main()
{
Derived d;
BaseA* pa = &d;//父类指针指向子类
BaseB* pb = &d;//父类指针指向子类
//dynamic_cast强制类型转换
//一用于有继承关系的类指针间的转换
//一用于有交叉关系的类指针间的转换
//一具有类型检查的功能
//一需要虚函数的支持
cout << "sizeof(BaseA) = " << sizeof(BaseA) << endl;
cout << "sizeof(BaseB) = " << sizeof(BaseB) << endl;
cout << "sizeof(d) = " << sizeof(d) << endl;//2个虚函数表
cout << "Using pa to call funcA()..." << endl;
pa->funcA();
cout << "Using pb to call funcB()..." << endl;
pb->funcB();
BaseB* pbe = (BaseB*)pa; // oops!!
cout << "(BaseB*)pa-Using pbe to call funcB()..." << endl;
pbe->funcB();
BaseB* pbc = dynamic_cast<BaseB*>(pa);
cout << "dynamic_cast<BaseB*>(pa)-Using pbc to call funcB()..." << endl;
pbc->funcB();
cout << endl;
cout << "pa = " << pa << endl;
cout << "pb = " << pb << endl;
cout << "pbe = " << pbe << endl;
cout << "pbc = " << pbc << endl;
return 0;
}
/*
sizeof(BaseA) = 8
sizeof(BaseB) = 8
sizeof(d) = 16
Using pa to call funcA()...
BaseA::funcA()
Using pb to call funcB()...
BaseB::funcB()
(BaseB*)pa-Using pbe to call funcB()...
BaseA::funcA()
dynamic_cast<BaseB*>(pa)-Using pbc to call funcB()...
BaseB::funcB()
pa = 0x61fdf0
pb = 0x61fdf8
pbe = 0x61fdf0
pbc = 0x61fdf8
*/
实验-正确使用多重继承:单继承某类+多接口实现
#include <iostream>
#include <string>
using namespace std;
class Base
{
protected:
int mi;
public:
Base(int i)
{
mi = i;
}
int getI()
{
return mi;
}
bool equal(Base* obj)
{
return (this == obj);
}
};
class Interface1//接口1
{
public:
virtual void add(int i) = 0;
virtual void minus(int i) = 0;
};
class Interface2//接口2
{
public:
virtual void multiply(int i) = 0;
virtual void divide(int i) = 0;
};
class Derived : public Base, public Interface1, public Interface2//多重继承
{
public:
Derived(int i) : Base(i)
{
}
void add(int i)
{
mi += i;
}
void minus(int i)
{
mi -= i;
}
void multiply(int i)
{
mi *= i;
}
void divide(int i)
{
if( i != 0 )
{
mi /= i;
}
}
};
int main()
{
Derived d(100);
Derived* p = &d;
Interface1* pInt1 = &d;
Interface2* pInt2 = &d;
cout << "p->getI() = " << p->getI() << endl; // 100
pInt1->add(10);
pInt2->divide(11);
pInt1->minus(5);
pInt2->multiply(8);
cout << "p->getI() = " << p->getI() << endl; // 40
cout << endl;
cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
return 0;
}
/*
p->getI() = 100
p->getI() = 40
pInt1 == p : 1
pInt2 == p : 1
*/
正确使用多重继承
■一些有用的工程建设议
一先继承一个交类,然后实现多个接口
一父类中提供equal()成员函数
一equal()成员函数用于判断指针是否指向当前对象
一与多重继承相关的强制类型转换用dynamic_cast完成
小结
多继承中可能出现多人虚函数表指针
与多重继承相关的强制类型转换用dynamic_cast完成
工程开发中采用“单继承多接口”的方式使用多继承
父类提供成员函数用于判断指针是否指向当前对象
第55课 - 经典问题解析四
关于动态内存分配
new和malloc的区别是什么? delete和free的区别是什么?
■new关键字与malloc函数的区别
new关键字是C++的一部分
malloc是由C库提供的函数
-new以具体类型为单位进行内存分配
-malloc以字节为单位进行内存分配
new在申请内存空间时可进行初始化
-malloc仅根据需要申请定量的内存空间
实验-new&malloc
#include <iostream>
#include <string>
#include <cstdlib>//注释后 malloc报错
using namespace std;
class Test
{
int* mp;
public:
Test()
{
cout << "Test::Test()" << endl;
mp = new int(100);
cout << *mp << endl;
}
~Test()
{
delete mp;
cout << "~Test::Test()" << endl;
}
};
int main()
{
cout << "before Test* pn = new Test " << endl;
Test* pn = new Test;
cout << "after Test* pn = new Test " << endl;
cout << endl;
cout << "before Test* pm = (Test*)malloc(sizeof(Test)) " << endl;
Test* pm = (Test*)malloc(sizeof(Test));
cout << "after Test* pm = (Test*)malloc(sizeof(Test)) " << endl;
cout << endl;
cout << "before delete pn " << endl;
delete pn;
cout << "after delete pn " << endl;
cout << endl;
cout << "before free(pm) " << endl;
free(pm);
cout << "after free(pm)" << endl;
return 0;
}
/*
before Test* pn = new Test
Test::Test()
100
after Test* pn = new Test
before Test* pm = (Test*)malloc(sizeof(Test))
after Test* pm = (Test*)malloc(sizeof(Test))
before delete pn
~Test::Test()
after delete pn
before free(pm)
after free(pm)
*/
关于动态内存分配
■delete和free的区别
-delete在所有C++编译器中都被支持
一free在某些系统开发中是不能调用
一delete能够触发析构函数的调用
-free仅归还之前分配的内存空间
一对象的销毁只能使用delete
-free不适合面向对象开发
构造函数是否可以成为虚函数?析构函数是否可以成为虚函数?
■构造函数不可能成为虚函数
一在构造函数执行结束后,虚函数表指针才会被正确的初始化
■析构函数可以成为虚函数
一建议在设计类时将析构函数声明为虚函数
实验-构造和析构可以是虚函数吗&构造和析构中不会发生多态
#include <iostream>
#include <string>
using namespace std;
class Base//父类
{
public:
Base()//构造函数 virtual Base() // error: constructors cannot be declared 'virtual' [-fpermissive]
{
cout << "Base()" << endl;
func();//测试会不会发生多态
}
virtual void func() //虚函数 构造函数和析构函数中不能发生多态行为
{
cout << "Base::func()" << endl;
}
virtual ~Base()//虚函数析构函数
{
func();//测试会不会发生多态
cout << "~Base()" << endl;
}
};
class Derived : public Base//子类
{
public:
Derived()
{
cout << "Derived()" << endl;
func();//测试会不会发生多态
}
virtual void func()//
{
cout << "Derived::func()" << endl;
}
~Derived()
{
func();//测试会不会发生多态
cout << "~Derived()" << endl;
}
};
int main()
{
Base* p = new Derived();//父类指针指向子类对象
// ...
delete p;
return 0;
}
/*
父类的析构函数不设置为虚函数输出:
Base()
Derived()
~Base()
期望释放的子类,父类
父类的析构函数设置为虚函数输出
Base()
Derived()
~Derived() //不会跳过子类对象的析构
~Base()
父类的构造和析构函数调用虚函数 func() 的输出
Base()
Base::func() 未发生多态,直接调用类内部的函数
Derived()
~Derived()
Base::func() 未发生多态,直接调用类内部的函数
~Base()
父类、子类的构造和析构函数调用虚函数 func() 的输出
Base()
Base::func()
Derived()
Derived::func() 未发生多态,直接调用类内部的函数
Derived::func() 未发生多态,直接调用类内部的函数
~Derived()
Base::func()
~Base()
*/
构造函数中是否可以发生多态?析构函数中是否可以发生多态?
构造函数中不可能发生多态行为
一在构造函数执行时,虚函数表指针未被正确初始化
析构函数中不可能发生多态行为
一在析构函数执行时,虚函数表指针已经被销毁
继承中如何正确的使用强制类型转换?
构造函数和析构函数中不能发生多态行为,只调用当前类中定义的函数版本!!
关于继承中的强制类型转换
■编译器会检查dynamic_cast的使用是否正确
■类型转换的结果只可能在运行阶段才能得到
关于继承中的强制类型转换
■dynamic_cast是与继承相关的类型转换关键字
■dynamic_cast要求相关的类中必须有虚函数
■用于有直接或者间接继承关系的指针(引用)之间
-指针:
·转换成功:得到目标类型的指钳
·转换失败:得到一个空指针
-引用:
·转换成功:得到目标类型的引用·
转换失败:得到一个异常操作信息
实验-dynamic_cast的使用
#include <iostream>
#include <string>
using namespace std;
class Base//父类
{
public:
Base()
{
cout << "Base::Base()" << endl;
}
//~Base()未设置为虚函数
virtual ~Base()//设置为虚函数
{
cout << "Base::~Base()" << endl;
}
};
class Derived : public Base//子类
{
};
int main()
{
//case1:
//Base* p = new Derived;//父类指针指向子类
//Derived* pd = dynamic_cast<Derived*>(p);//父类内部没有虚函数error: cannot dynamic_cast 'p' (of type 'class Base*') to type 'class Derived*' (source type is not polymorphic)
//Derived* pd = dynamic_cast<Derived*>(p);//有虚函数后 编译通过
//cout << "pd = " << pd << endl;//0x632180
//dynamic_cast强制类型转换
//一用于有继承关系的类指针间的转换
//一用于有交叉关系的类指针间的转换
//一具有类型检查的功能
//一需要虚函数的支持
//case2:
Base* p = new Base;//父类指针指向父类
Derived* pd = dynamic_cast<Derived*>(p);
cout << "pd = " << pd << endl;//0 转换失败了 子类指针不能指向父类
if( pd != NULL )
{
cout << "pd = " << pd << endl;
}
else
{
cout << "Cast error!" << endl;
}
delete p;/**/
return 0;
}
小结
new/delete会触发构造函数或者析构函数的调用
构造函数不能成为虚函数
析构函数可以成为虚函数
构造函数和析构函数中都无法产生多态行为
dynamic_cast是与继承相关的专用转换关键字