(一四三)类设计回顾

简介:

编译器自动生成的特殊成员函数:

默认构造函数:

当构造函数无参数、或所有参数都有默认值时(二者不能同时存在),则是默认构造函数。

 

自动生成的默认构造函数,将调用基类的默认构造函数;如果类的数据成员是另一个类的对象,那么这个数据成员在生成的时候,也会调用其默认构造函数。

 

假如定义了某个构造函数,那么编译器将不会定义默认构造函数。

 

假如类数据成员有指针,应该在默认构造函数中正确为其分配内存地址。

 

 

复制构造函数:

原型:类名(const 类名& );

使用复制构造函数的四种情况(关键是在声明一个类并初始化时):

①将新对象初始化为一个同类对象(如Man a=b; Man是类名,下同);

②按值将对象传递给函数(如void show(Man a));

③函数按值返回对象(如Man show(););

④编译器生成临时对象(临时对象通常在以下情况产生:①类型转换;②按值返回;③按值传递;④对象定义);

 

具体情况:

①类里没有声明复制构造函数,也没用使用——编译器提供复制构造函数原型,但不提供定义;

 

②类中没有声明,但是使用了——编译器提供复制构造函数的原型和定义(数据成员按值传递);

 

③类中声明了,使用类提供的复制构造函数。

 

 

赋值运算符:

在声明类的时候,调用构造函数或复制构造函数。

而当类对象被声明之后,将一个类对象赋值给另一个类对象时,才使用赋值运算符。

原型:类名operator=(const 类名& );

 

①默认情况下,将使用逐成员按值传递;

 

②假如数据成员是另一个类对象,则在按值传递的时候,调用该类的赋值运算符(如果该类没有,则使用该类的默认赋值运算符);

 

③对于指针(由new分配内存)作为数据成员时,由于构造函数需要显式声明,同样,赋值运算符也需要显式声明以处理其情况;

 

如果要将另一个类型赋值给类对象,方法①是定义该类型作为赋值运算符的参数;方法②是使用转换函数(之构造函数,将其他类型转换为临时类对象,然后通过临时类对象赋值给类对象);

 

 

 

其他类方法:

构造函数:

特点是声明一个新对象时调用(视情况调用默认构造函数、构造函数、或者是复制构造函数)。

 

构造函数不被派生类继承,但派生类的构造函数会默认调用基类的构造函数。

 

如果类的数据成员使用动态内存,那么应显式的声明构造函数、复制构造函数、赋值运算符和析构函数。

 

 

析构函数:

①基类的析构函数应该定义为虚的,即使没有任何内容,也至少应该有一个虚的析构函数。

 

②如果构造函数使用new来分配动态内存给数据成员,那么析构函数应该使用对应的delete来释放内存。

 

③需要显式的使用析构函数的情况很少,使用new定位运算符创建的类对象,是少数几种需要显式的调用析构函数的情况之一(因为不能通过直接delete类对象的方式,来调用析构函数)。

 

 

 

转换:

使用一个参数就可以调用的构造函数,定义了从参数类型到类类型的转换。

例如将一个int类型作为类构造函数的参数,那么这个类对象可以等于int类型(利用构造函数,然后调用赋值运算符)。

 

如果需要禁止这种情况发生,可以在构造函数前面加上关键字explicit,来禁止隐式转换,于是只有显式的调用对应的构造函数时,才能进行转换。

 

将类对象转换为其他类型的转换函数,可以是没有参数的类成员函数(例如operator int());也可以是返回类型被声明为目标类型的类成员函数不懂)。

 

 

 

按值传递、按引用传递,返回对象和返回引用:

使用引用的好处:

①节省内存开支(不需要创建临时对象);

 

②对于指针,如返回临时对象的话,应显式声明复制构造函数,但返回引用就不用;

 

缺点:

①不能对函数内部创造的对象返回引用(因为离开函数块时该引用的原型会被销毁);

 

通常来说,如果是函数内部创造的对象,然后返回它,就应该使用返回对象而非返回引用。否则,应尽量使用返回引用(如果要求其不能成为左值,则加上关键字const);

 

 

 

使用const

作用:

①可以确保类方法不修改对象的数据成员(在参数列表的括号后加const);

 

②确保类方法不修改调用的参数(在参数类型名前加const);

 

③确保类方法的返回值不被修改(在返回值的类型名前加cosnt);

 

 

 

 

公有继承的考虑因素:

is-a关系:

要遵循is-a关系,如果派生类不是一种特殊的基类,则不要使用公有派生。(不懂)

感觉大概意思是说,一个类作为基类还是作为另一个类的成员对象,要看情况。有些时候适合做基类(例如派生类是基类的一种),有些时候适合做成员对象(例如派生类是基类的一部分)。

 

可以创建具有纯虚函数的抽象基类,然后派生出其他类。

 

基类的指针、引用可以指向派生类对象,但不能反过来。

 

 

 

什么不能被继承:

①构造函数:但在派生类构造函数时会自动调用基类的构造函数;

 

②析构函数:派生类析构函数也会自动调用基类的析构函数;

 

③赋值运算符:视情况而定,有时候可以使用默认的,有时候需要自定义,并显式调用基类的赋值运算符函数(基类名::operator=(参数对象))。

 

 

赋值运算符:

①默认赋值运算符版本,将逐成员按值传递;

 

②派生类的赋值运算符如果是默认版本,将对基类部分调用基类的赋值运算符,对派生类新增数据成员按值传递;

 

③如果自定义派生类赋值运算符,那么需要显式的声明基类的赋值运算符以复制基类的部分;

 

④遇见new来分配内存,应自定义赋值运算符;

 

⑤默认情况下,派生类对象赋值给基类对象,相当于将派生类对象强制转换为基类并赋值(相反则不行,只能调用参数合适的构造函数,或参数合适的赋值运算符)。

 

 

 

私有成员和保护成员:

对于派生类而言,保护成员类似公有成员;

对于外界而言,保护成员类似私有成员。

 

 

 

虚方法:

格式是函数原型(函数定义不需要)加关键字virtual

①基类的析构函数需要虚方法;

 

②对于派生类需要重新定义的方法,基类使用虚方法(派生类可以不用,但用的话可以明确表示基类该方法也是虚的);

 

③使用虚方法时,受传递的对象所影响(按引用、指针传递,和按值传递的结果是不一样的);

 

 

析构函数:

公有继承的析构函数应该是虚的。

 

 

 

友元函数:

派生类不继承友元函数,但可以显式的调用友元函数。方法是 类名::基类友元函数 。或者是使用强制类型转换。

 

 

 

关于使用基类方法的说明:

①某方法如果派生类没有重新定义,则使用基类的;

 

②派生类构造函数自动调用基类的,默认调用默认构造函数,除非更改声明调用的构造函数(例如原本是调用默认构造函数的,1个参数。改用另一个构造函数,2个参数的);

 

③一般情况下,派生类复制构造函数会自动使用基类的复制构造函数,除非自己显式的在派生类复制构造函数使用其他构造函数;

 

④派生类的方法中,可以通过作用域解析运算符(双冒号)显式的调用基类的方法(前提不是私有的);

 

 

 

其他(附表):


目录
相关文章
|
6月前
|
设计模式 安全 Java
【C++】特殊类设计
【C++】特殊类设计
|
4月前
|
设计模式 安全 编译器
【C++11】特殊类设计
【C++11】特殊类设计
63 10
|
设计模式 安全 Java
|
6月前
|
算法 编译器 C语言
【C/C++ 编程题 01】用C++设计一个不能被继承的类
【C/C++ 编程题 01】用C++设计一个不能被继承的类
69 0
|
6月前
|
编译器 C++
【C++】—— 特殊类设计
【C++】—— 特殊类设计
|
6月前
|
设计模式 Java C++
C++之特殊类的设计
C++之特殊类的设计
29 0
|
11月前
|
设计模式 安全 编译器
C++特殊类设计
C++特殊类设计
|
编译器 C++
【C++】特殊类设计(上)
【C++】特殊类设计(上)
|
设计模式 安全 Java
【C++】特殊类设计(下)
【C++】特殊类设计(下)
|
编译器
特殊类设计
特殊类设计
96 0