【C++面向对象高级编程】知识点总结(3)

类之间的关系(三大类)

概念

在面向对象中类之间的关系分为三类:继承,复合,委托。

和我们熟知的面向对象的三大特性:封装,继承,多态有一定的联系。

关于复合

复合(composition),表示拥有(has a)。也就是在一个类中包含另一个类。

比如在STL中所说的stack和queue就是在内部拥有一个deque,叫做容器适配器。
请添加图片描述

复合关系下的构造与析构

当两个类属于复合关系时:
请添加图片描述

构造是要由内而外的,也就是需要先构造包含的类componment,再构造拥有者container。

析构刚好相反,是由外而内的。也就是先析构container再析构component。

关于委托

委托其实也是一种拥有。只不过是指针层面上的拥有。

就像你想让另一个类做些什么事情,但是又不想影响那个类本身的实现,想视情况而定,这就是委托。

class StringRep;
class String{
public:
	String();
	String(const char* s);
	String(const String& s);
	String &operator=(const String& s);
	~String();
	...
private:
	StringRep* rep;
}

这里StringRep的具体实现是不受影响的。

class StringRep{
friend class String;
	StringRep(const char* s);
	~StringRep();
	int count;
	char* rep;
};

关于继承

继承(inheritance)是子类与父类的关系,是一种“是”(is a)。

struct _list_node_base
{
...
}
struct _list_node:public _list_node_base
{
...
}

list_node继承于list_node_base,我们也说list_ node_base是list_node

继承关系下的构造与析构

与复合关系类似,继承关系的构造也是由内而外的,
请添加图片描述

构造时先调用base的构造再调用derived的构造。

析构时先调用自己的析构,再调用base的析构。

继承与虚函数

在继承这个部分要特别注意虚函数,以上面的base与derived的关系举例。

如果是base定义了非虚函数,那么不希望derived重新定义。

如果是base定义了虚函数,那么希望derived重新定义,并且已经有默认的定义了。

如果是base定义了纯虚函数,那么希望derived一定要重新定义(因为没有默认定义)

class A{
public:
	virtual void draw() const=0;//纯虚函数
	virtual void error(const std::string& msg);//虚函数
	int objectID() const;//非虚函数
	...
}

关于虚函数

虚函数详解

关于explicit关键字

explicit关键字用来修饰类的构造函数,主要作用是防止其隐式调用,告诉编译器必须进行显式调用,也就是不要自动进行类型转换。

举个例子

class A{
public:
	A(int a):aaa(a){}
private:
	int aaa;
};

比如这样一个类,构造函数的参数是int类型。

A num=10;

当你这样构造时,没有我们前面说的赋值构造,编译器就会进行一个隐式的转换,因为10也是int类型的。所以可以成功。

当我们在构造前加上explicit时,编译器就会报错,提示无法从int转换为A。

关于const

我们在第一篇中提到了const

【C++面向对象高级编程】知识点总结(1)

有一个较为简单的使用方法

在你不想改变值的情况下就加上const,可以在函数后或者变量之前。

 class complex{
 public:    
 	complex (double r=0,double i=0)    :re(r),im(i){}
 	double real() const {return re;}
 	double imag() const {return im;}
 private:
 	double re,im;
 	}

为了不死记硬背,我们来理解一下这个机制。

首先,类的对象分为加const的常量对象和不加const的非常量对象。

成员函数也分为加const的成员函数和不加const的成员函数。

这里用一个表格来理解。

请添加图片描述

  • 常量对象
    • 不想让数据成员有变动,而加const的成员函数可以保证不改变数据,所以可以调用。
    • 不想让数据成员有变动,而不加const的成员函数可以保证不改变数据,所以不可以调用。
  • 非常量对象
    • 可变动数据成员,加不加const都可以,因为不在乎是否改变。

这样解释就可以理解了。

举个例子:

const String str("hello");
str.print();

这时候对象为const类型,只可以调用const的成员函数,如果非const就不能保证不修改数据,所以以上代码会报错。

当然还有一种特殊情况,就是当成员函数的const和不加const两个版本同时存在时。

比如

charT operator[](size_type pos)const{...}
reference operator[](size_type pos){...}

这时候设计者会想让const和非const区分开,因为非const的对象既可以调用const成员函数也可以调用非const成员函数,会出现错误。

所以这时候有一条规定:当这两者同时存在时,const的对象只能调用const的成员函数,非const的对象只能调用非const的成员函数。

关于引用

引用本质上其实也是指针构成的,其实是在指针的基础上做了一些包装。

这里举一个非常生动的例子来说明值,指针,引用之间的关系。

请添加图片描述

当我们为变量x赋值后,它会在内存中占用空间。

当我们用指针p指向x的地址时,这个指向int类型的指针会占用空间。

当我们用引用r指向x时,这个指向int类型的引用会占用空间。同理,引用r2也是一样的,指向r的空间。

可以看出引用和指针本质上是一样的,只是引用做了一些包装。

让引用和原值的大小以及地址都相同,并且不能代表其他变量,这就是包装后的结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值