
接口继承 vs 实现继承
接口继承:如子类重写父类的纯虚函数---每个子类都继承了这样一个“接口”,每个子类都必须给这个函数一个独有的定义,否则这个子类将还是一个抽象类
实现继承:基类定义了一个有具体行为的虚函数,而非纯虚函数
这个时候子类就有选择:它既可以选择直接继承这个虚函数,直接把基类的实现继承下来 直接用他
也可以自己override重写
一、Copy Control Copy and Swap
首先别忘了inline是干嘛的:C++ 中的 inline 用法 | 菜鸟教程
上面是algorithm库中swap函数的实现
这种交换方式的三个等号分别是三次拷贝 *** 作,效率低下
我们打算这样做:
仔细看上面,swap变成了Vector类的成员函数,然后对要交换的成员函数/指针,我们再调用std::swap来交换即可(std::swap用来交换数字,指针 效率还是很高的)
其中,noexcept关键字表示“不抛出异常”
利用上面在类中写的的swap,我们可以有一种安全简洁的拷贝复制运算符的实现方式:
这种方式让 拷贝赋值运算符 依赖于 拷贝构造函数
Prevent Copying 禁止拷贝
C++11后,用 =delete 即可达到目的
这里考虑另外一种方法:涉及到 空基类优化 Empty Base Optimization (EBO)
注意基类Uncopyable 没有说访问权限是什么,默认private
关键是:private作用域下只声明不定义
子类继承之后,编译器无法自动生成拷贝函数
但是上面方法有个小问题:Uncopyable类型的指针或者引用 可以 与 任何 Uncopyable子类的对象产生绑定关系!别忘了 Uncopyable不同子类之间的对象是没有什么关系的!我们要避免这种情况!
问题关键就是:继承关系现在是公开的,因为是public继承
所以我们只需要用private继承即可
私有继承的理解:继承关系是个秘密
二、Resource-managing Classes 资源管理类 Surrogate 代理
正确的方法如下:
注意,上面重写的时候,返回值分别改成Rectangle*, Circle* 也是正确的重写方式
因为别忘了重写虽然要求identical,但是有一点例外:返回类型既可以是identical,也可以是covariant with(协变)----忘了?看上次gkxx recitation9!
#include
#define PI 3.141592653589793
using namespace std;
class Shape_base{
public:
virtual double perimeter() const = 0;
virtual double area() const = 0;
virtual ~Shape_base() = default;
virtual Shape_base* clone() const = 0;
};
class Rectangle : public Shape_base{
double height, width;
public:
Rectangle(double h, double w):height(h),width(w) {}
virtual double perimeter() const override{
return 2 * (height + width);
}
virtual double area() const override{
return height * width;
}
virtual Rectangle* clone() const override{
return new Rectangle{height,width};//这里的花括号说是一种modern风格,用圆括号也行
}
};
class Circle:public Shape_base{
double radius;
public:
Circle(double r):radius(r){}
virtual double perimeter() const override{
return 2 * PI * radius;
}
virtual double area() const override{
return PI * radius * radius;
}
virtual Circle* clone() const override{
return new Circle{radius};
}
};
int main()
{
system("pause");
return 0;
}
Surrogate 代理定义
然后我们考虑Shape对象之间的拷贝 *** 作改如何实现
上面Shape类中的stretech函数并没有修改Shape类的成员
注意,Shape类中的成员是Shape_base* bp,即指针
只有这个指针被修改 才叫做修改
而stretch函数仅仅是调用了这个指针所指对象的stretch函数,所以编译器中我们加上const是没有问题的
但是从逻辑上讲,或者说从最终的效果来看,我们最后调用的,这个指针所指对象的stretch函数,确确实实修改了所指对象的成员参数(使长方形或者圆的数据增大m倍)
所以从逻辑上讲,我们这里不用写const
下面是代理类的使用
我们开了一个装有Shape类型的数组
我们不需要人为再new,delete,并且还有动态绑定的特性,非常方便
总之,代理既有内存管理的功能,也有动态绑定的特性
#include
#define PI 3.141592653589793
using namespace std;
class Shape_base{
friend class Shape;
virtual double perimeter() const = 0;
virtual double area() const = 0;
virtual Shape_base* clone() const = 0;
virtual void stretch(double) = 0;
protected:
virtual ~Shape_base() = default;
};
class Shape{
friend Shape make_rectangle(double, double);
friend Shape make_circle(double);
Shape_base *bp;
public:
Shape():bp(nullptr){}
double perimeter() const {
return bp->perimeter();
}
double area() const {
return bp->area();
}
bool is_null() const{
return !bp;
}
void stretch(double m){
bp->stretch(m);
}
Shape(const Shape &other)
:bp(other.bp? other.bp->clone():nullptr){}
Shape& operator=(const Shape &other){
auto copy = other.bp ? other.bp->clone() : nullptr;
delete bp;//不能一上来直接delete bp是为了考虑other就是自己的情况
bp = copy;
return *this;
}
~Shape() {
delete bp;
}
private:
Shape(Shape_base* p):bp(p){}
};
class Rectangle : public Shape_base{
friend Shape make_rectangle(double, double);
double height, width;
Rectangle(double h, double w):height(h),width(w) {}
virtual double perimeter() const override{
return 2 * (height + width);
}
virtual double area() const override{
return height * width;
}
virtual Rectangle* clone() const override{
return new Rectangle{height,width};//这里的花括号说是一种modern风格,用圆括号也行
}
virtual void stretch(double m) override{
height *= m;
width *= m;
}
};
inline Shape make_rectangle(double h,double w){
return new Rectangle{h, w};
}
class Circle:public Shape_base{
friend Shape make_circle(double);
double radius;
Circle(double r):radius(r){}
virtual double perimeter() const override{
return 2 * PI * radius;
}
virtual double area() const override{
return PI * radius * radius;
}
virtual Circle* clone() const override{
return new Circle{radius};
}
virtual void stretch(double m) override{
radius *= m;
}
};
inline Shape make_circle(double r){
return new Circle(r);
}
int main()
{
system("pause");
return 0;
}
新的术语:
值语义 Value Semantics
引用语义 Refenrence Semantics
我们刚刚实现的Shape就是值语义
Reference-counting Handles 引用计数句柄
handle 句柄
我们的懒惰思想是:只有当要进行像stretch函数这样,要修改成员的 *** 作时
我们再来根据值语义来拷贝
其他时候引用语义就够了
最后几句话大概就是介绍了一下:这节课讲的资源管理类的部分主要来自于Ruminations on C++的第5-7章;第8章是用reference-counting handle实现第五次作业的第三题(也是这道题的idea来源);第9章和第10章实现了一个更有意思的例子。另外,Effective C++的Item 13-17讲了资源管理,其中Item 15和17说了一些我没有提到的注意事项。此外自C++11起标准库中以“智能指针”的形式实现了这些资源管理类,在C++ Primer第12章第1节中解释了这些智能指针的基本用法,在Effective Modern C++ Item 18-22中具体讲解了在什么场合下该如何使用智能指针。
去写作业喽~
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)