承接上篇
1.抽象基类
不能被实例化的基类被称为抽象基类,这样的基类只有一个用途,那就是用它来派生出其他类,可使用纯虚函数的声明来创建纯虚函数。纯虚函数在虚函数表中不指向任何函数成员。
class Figure
{
public:
virtual void GetArea() = 0;//纯虚函数
}; //构成抽象类
抽象类只含有纯虚函数
class Circle : public Figure
{
void GetArea() { .. .. . . .} ;
}
class Triangle : public Figure
{
void GetArea() { .. .. . . .} ;
}
分别定义后在主函数调用时会相应调用类中相应内容
值得注意的是:声明抽象基类后 1. 派生类必须声明相应的方法;2. 不能再对这个基类实例化
2.继承与动态分配内存
在书上,利用的是char类型的数组在类中的动态分配完成的,因为涉及到了动态分配内存,所以在定义复制构造函数时进行了动态分配内存,在动态分配内存时也考虑到了内存大小问题,调用了strlen(),strcpy(),strncpy(),等一系列函数来完成操作,在调用delete时也有很多注意点,在动用复制构造函数时需要在再次分配内存之前调用delete来删除先前内容,注意new的使用,不能造成内存泄露
在虚析构函数实现的时候,并不需要virtual关键字,且定义了之后不要自定义非虚析构
3.派生类若不显示调用基类的构造函数,则系统会自动调用基类的默认构造函数,如果基类没有定义构造函数(一般不会发生)则报错
4.C++默认构造函数、拷贝构造函数、拷贝赋值函数
这里做一下说明,因为不同版本命名方法不同
@1拷贝赋值函数=复制函数(指重载)
@2拷贝构造函数就是把类对象作为参数的构造函数
@3复制构造函数=拷贝构造函数
@复制=赋值,少用容易混
见代码-网上抄来的
#include <iostream>
using namespace std;
class Base {
public:
int var;
~Base() {}
// 默认构造函数
Base() {
cout << "调用默认构造函数" << endl;
}
// 拷贝构造函数
Base(const Base & outer)
: var(outer.var) {
cout << "调用拷贝构造函数" << endl;
}
// 拷贝赋值函数
Base & operator =(const Base & outer) {
cout << "调用拷贝赋值函数" << endl;
if (this != &outer) {
this->var = outer.var;
}
return *this;
}
};
int main() {
Base a; // 默认构造函数
Base b(a); // 拷贝构造函数
Base c = a; // !任然是拷贝构造函数
a = c = b; // 拷贝赋值函数,注意拷贝赋值函数不算"构造"函数
}
在类中有new的动态分配内存时自定义很重要
class A
{
int *p;
public:
A()
{
p = new int[10];
}
~A()
{
delete p;
}
A(A &x) //定义拷贝构造函数,如果不定义产生情况见f函数中说明
//(如果这里不引用又是作死3次delete)事实上编译器甚至不允许不采取引用
{
p = new int[10];
*p = *(x.p);
}
};
void f(A a) //如果不定义这里会简单的复制,p=x.p,导致最后delete两次,运行时出错
//还有一种解决办法是这里设置引用 void f(A &a)
{
cout << "Yes" << endl;
}
int main()
{
A a;
f(a); //这里调用函数的拷贝构造,函数传参(非引用)默认传副本
}
5.再探友元函数
#include<iostream>
using namespace std ;
class A
{
public:
friend void f () {cout<<"yes" ;}
};
int main()
{
f() ; //错误,没有声明f();也就是说友元函数必须在类外定义
return 0 ;
}
==关于书上说的友元函数基类派生类的互通关系,我觉得很奇怪,如果不是想要二次定义的话就根本不会产生任何冲突,书上说到,如果要强制转换(const baseDMA&)hs 来达到目的,友元函数全部是在类外定义的,也就是说变成通用函数,在派生类里也是同样可用,所以我觉得覆盖定义也毫无意义