每周100个C++知识点(备忘录)(五)

001-010 string类的一些底层技术

1.C++11引入的空指针关键字:nullptr

2.对于比较两个字符串的“大小”,即第一个字符串是否在第二个字符串之前,可以重载<、>、==运算符,使用的技术是strcmp()函数。重载函数是:bool operator < (const string &st1, const string &st2);

3.将比较函数作为友元,可以完成C-风格字符串(不是类,没有方法,必须用友元函数)和string类的比较;比较时,C字符串被string类的某个构造函数转化为string类型

4.中括号运算符[]的操作数是2,第一个操作数是数组名,第二个操作数是字符索引。string重载C风格的[]运算符功能,即在重载返回数据的位置上返回str[i],语句转换为xxx.operator[i]

5.调用这些方法访问了私有数据(甚至更改了私有数据),如果不能更改私有数据,则需要将成员函数添加const修饰符

6.静态类函数成员,函数声明必须包含关键字static,但如果函数定义是独立的,则不能包含static关键字。对于静态类成员函数,不能通过对象调用(不能使用this指针),使用时用域解析运算符访问;它也不能使用非静态数据成员,因为静态成员函数不与特定的对象相关联

(在构造函数中使用new注意事项)
7.在构造函数中使用了new,则应在析构函数中使用delete;在构造函数中使用new[],则应在析构函数中使用delete[];如果有多个构造函数,则必须使用相同的方式使用new,每个构造函数都必须与析构函数兼容,如果构造函数中不使用new,则需要将指针声明为空的,可以是0或nullptr

8.应该定义复制构造函数,通过深度复制将一个对象初始化为另一个对象,复制构造函数应当分配足够的空间来存储复制的数据,并复制数据;还应该更新受影响的静态类成员

9.应该定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象,需要检查自我赋值情况,释放成员指针以前指向的内存,复制数据而不仅仅是地址,返回一个指向调用对象的引用

10.包含类成员的类的逐成员复制:这种情况下,默认的逐成员复制和赋值,将调用自己的复制构造函数和赋值运算符,但如果其他成员也有,则需要显式地调用两个(多个)类的复制构造函数和赋值运算符

011-015 返回对象的事项

11.返回指向const对象的引用:旨在于提高效率,如果函数返回传递给它的对象(通过调用对象的方法或将对象作为参数),可以通过引用提高效率。返回对象将调用复制构造函数,返回引用不会;引用指向的对象应该在函数调用执行时存在

12.返回指向非const对象的引用:重载赋值运算符/与cout一起使用的<<运算符,前者旨在提高效率,后者必须这样做

13.返回对象:如果被返回的对象是被调用函数中的局部变量,则不可以按引用方式返回它,因为执行完毕会调用析构函数。被重载的算术运算符属于这一类(返回一个新的临时变量),使用复制构造函数是无法避免的

14.返回const对象:不希望出现class_1+class_2=temp这种“错误的”生成临时变量的语句被认为是合法语句时,返回const对象

15.总结:函数要返回局部对象,将使用复制构造函数生成返回的对象;如果方法或函数要返回一个没有共有复制构造函数的类的对象,它必须返回一个指向这种对象的引用。可以返回对象和引用的函数,选择返回引用的效率更高

016-020 new运算符、对象指针、定位new运算符

16.通常,如果Class_name是类,value的类型为Type_name,则Class_name * pclass=new Class_name(value);将调用构造函数Class_name(Type_name);,可能的转换:Class_name(const Type_name &);;如果不存在二义性,则将发生由原型匹配导致的转换,则Class_name * ptr=new Class_name将被调用

17.使用对象指针时,需要注意:①使用常规表示法来声明指向对象的指针:string * name;;②可以将指针初始化为指向已有的对象:string * first=&names[0];;③可以使用new来初始化指针,创建一个新的对象;④对类使用new将调用相应的类构造函数来初始化新创建的对象;⑤可以使用->运算符通过指针访问类方法;⑥可以对对象指针应用解除引用运算符* 来获得对象

18.常规定位new运算符和对象的定位new运算符用法有所不同。如果对于同一个内存块,连续使用定位new运算符创建多个对象,则后续对象不可直接创建,应当在原来内存定位的基础上偏移n个对象的大小

19.使用delete时,并不是使用类的析构函数,因为该内存块是由创建该内存块的new运算符管理的,所以要使用与之对应的delete(或delete[]),因此需要对对象显式调用析构函数:pc->~Class_name();

20.★★★★★★显式调用析构函数时,应当首先调用后创建的对象的,这是因为后创建的对象可能和前面创建的对象有关联,同时应当顺序释放内存

021-034 队列模拟

21.队列是一种抽象的数据类型(ADT),它类似于栈,但栈是在同一端进行添加和删除,是一种后进先出(LIFO)的结构;队列是在两端进行添加和删除,是一种先进先出(FIFO)的结构

22.队列的描述使用链表,链表是一个结构,包含数据部分和指针部分,指针指向了下一个数据的地址。链表的最后一个节点中的指针被设置为nullptr

23.在类中嵌套结构或类声明,可以是其作用域为整个类。即它可以来声明类成员,也可以将它作为类方法中的类型名称,但只能在类中使用,这样可以区分结构声明与某些全局声明或其他类中声明的同名结构发生冲突

24.如果声明是在类的私有部分进行的,则只能在这个类中使用被声明的类型,如果声明是在公有部分进行的,则可以从类的外部通过作用域解析符使用被声明的类型

25.对于类构造函数中的传入参数,如果它是常量,则不能进行赋值,但可以进行初始化。初始化的语法是Class::Class(type a):const_var(a),即如果数据成员的名称为mdata,要将它初始化为val,则初始化器为mdata(val)。初值可以是常量或构造函数的参数列表中的参数,不仅限于初始化常量

26.只有构造函数可以使用25中的初始化列表语法,对于const类成员,必须使用这种语法,对于被声明为引用的类成员,也必须使用这种语法

27.25和26的工作机理:进行构造函数调用时,首先在内存中获得了存储变量的位置,但是此时如果常量没有初始化,则无法进行常规变量的赋值,因此需要在调用时进行初始化;声明为引用的类成员,在使用时也没有进行初始化,因此必须在调用析构函数时进行初始化

28.这些初始化工作,是在对象创建时完成的,此时还没有执行括号中的任何代码,这种格式:①只能用于构造函数;②必须用这种格式来初始化非静态const数据成员;③必须使用这种格式来初始化引用数据成员

29.数据成员被初始化的顺序与它们出现在类声明中的顺序相同,与初始化器中的排列顺序无关。不能将成员初始化列表语法用于构造函数之外的其他方法

30.添加队列成员的步骤:①如果队列已满,则结束;②创建一个新的节点;③在节点中放入正确的值,并将节点的next指针设置为nullptr;④将项目计数+1;⑤将节点附加到队尾,首先将节点与列表中的另一个节点连接起来,通过将当前队尾节点的next指针指向新的队尾节点完成;然后将成员指针rear设置为指向新节点,使队列可以直接访问最后一个节点

31.删除队首项目的步骤:①如果队列为空,则结束;②将队列的第一个项目提供给调用函数,通过将前front节点中的数据部分复制到传递给方法的引用变量中实现;③将项目计数-1;④保存front节点的位置以后删除;⑤让节点出队;⑥删除以前的第一个节点;⑦如果链表为空,则rear设置为nullptr

32.析构函数要删除剩余的所有节点,因为类的方法可以清除节点,但是并不能保证队列在到期时为空,因此需要显式删除剩余的所有节点,从链表头开始,依次删除其中的每一个节点

33.如果队列需要复制、合并、截短等操作,则需要定义复制构造函数,不然只有副本的尾指针得到更新,还有更严重的后果。如果不需要,为了避免错误发生,可以定义伪私有方法,通过将构造函数放在私有定义中,定义伪私有方法,这样将不可直接进行队列赋值等操作(以及创建临时变量)

34.使用已知平均值的一定范围内的随机数:std::rand()*x/RAND_MAX<1,就是说平均每隔x次,这个值会有一次小于1,其中RAND_MAX是cstdlib头文件中定义的rand()函数可能返回的最大值

035-052 类继承与基类

35.面向对象编程的主要目的之一是提供可重用的代码。传统C函数库通过预定义、预编译的函数提供可重用性,厂商提供标准C库没有的专用C库,但函数库也有局限性,除非源厂提供源代码,否则不可以根据特定要求对函数进行拓展修改。即使提供源代码,修改也有很大的风险

36.C++提供了类库,类库由类声明和实现构成,比函数库更加完整。C++提供了比修改代码更好的方法来扩展和修改类,这种方法叫做类继承,能够从已有的类派生出新的类,而派生的类继承了原有类(基类)的特征,包括方法

37.继承要完成的工作:①可以在已有类的基础上添加功能;②可以给类添加数据;③可以修改类方法的行为

38.从一个类派生出另一个类时,原始类称为基类,继承类称为派生类

39.★★★★一个错误:C2679:二进制“<<”没有找到接受const std::string类型的右操作数的运算符(或没有可接受的转换),解决方案:加入string库,即添加#include

40.对于公有基类,通过其派生称为公有派生。派生类对象包含基类对象。使用公有派生,基类的公有成员将称为派生类的公有成员;基类的私有部分也将称为派生类的一部分,但只能通过基类的公有和保护方法访问

41.派生类将具有的特征:①派生类对象存储了基类的数据成员(派生类继承了基类的实现);②派生类对象可以使用基类的方法(派生类继承了基类的接口)。需要在继承特性中添加:①派生类自己的构造函数;②派生类可以根据需要添加额外的数据成员和成员函数

42.派生类的构造函数必须给新成员和继承的成员提供数据(两个方案,分别提供和提供新数据、老对象的指针)

43.派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问,具体地说,派生类构造函数必须使用基类构造函数。创建派生类对象时,程序首先创建基类对象。基类对象应当在程序进入派生类构造函数之前被创建,使用成员初始化列表语句完成

44.派生类构造函数的要点:①首先创建基类对象;②派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;③派生类构造函数应初始化派生类新增的数据成员

45.释放对象的顺序与创建对象的顺序相反,即首先执行派生类的析构函数,再执行基类的析构函数(自动)。基类的构造函数负责初始化继承的数据成员;派生类构造函数用于初始化新增数据成员,派生类的构造函数总是调用一个基类构造函数,可以使用初始化列表语法指明使用的基类构造函数,否则使用默认的构造函数

46.成员初始化列表语法:派生类构造函数可以使用初始化器列表机制将值传递给基类构造函数:derived::derived(type1 x,type2 y) : base(x,y) {…};其中derived是派生类,base是基类,x和y是基类构造函数使用的变量

47.除虚基类外,类只能将值传递回相邻的基类,但后者可以使用相同的机制将信息传递给相邻的基类,依此类推。如果没有在成员初始化列表中提供基类构造函数,程序将使用默认的基类构造函数,成员初始化列表只能用于构造函数

48.派生类对象可以使用基类的方法,条件是方法不是私有的;基类指针可以在不进行显式类型转换的情况下指向派生类对象,基类引用可以在不进行显式类型转换的情况下引用派生类对象;基类指针只能用于调用基类方法,不能调用派生类方法

49.不可以将基类对象和地址赋给派生类引用和指针,因为基类对象中没有派生类数据成员,也无法调用派生类的方法

50.基类引用和指针可以指向派生类对象,将会出现的结果是:①基类引用定义的函数或指针参数可用于基类对象或派生类对象;②对于形参为指向基类的指针的函数,也可以使用基类对象的地址或派生类对象的地址作为实参;③引用兼容性属性使得可以将基类对象初始化为派生类对象,即基类由隐式复制构造函数,可以通过派生类声明基类,也可以通过隐式重载赋值运算符,将派生类对象赋给基类对象

51.C++有3种继承方式,公有继承、保护继承和私有继承。公有继承是最常用的,是一种is-a关系,即派生类对象也是一个基类对象,可以对基类对象执行的任何操作,也可以对派生类对象执行

52.“香蕉是水果”:is-a关系;”午餐有香蕉“:has-a关系;”律师像鲨鱼“:is-like-a关系;”使用数组实现栈“:is-implemented-as-a关系;”使用打印机打印“:uses-a关系。C++中公有继承只使用is-a关系,其他关系可能带来混乱

053-061 多态公有继承

53.对于一个方法在派生类和基类中的行为是不同的,方法的行为取决于调用该方法的对象,这种行为称为多态。实现多态公有继承的方法:①在派生类中重新定义基类的方法;②使用虚方法

54.对于在每个类中功能相同的函数,只在基类中声明,多态方法在使用时,根据已创建按的对象的相应函数,自动进行选择

55.关键字virtual:如果方法是通过引用或指针而不是对象调用的,它将确定使用哪一种方法,如果没有关键字virtual,程序将根据引用类型或指针类型选择方法,如果使用了virtual,程序将根据引用或指针指向的对象的类型来选择方法

56.经常把在派生类中重新声明的方法声明为virtual的,这是因为,派生类可以使用基类指针或引用来指向,但是如果不使用virtual关键字,这种指针或引用将不能使用派生类的方法。为基类声明一个虚析构函数也是惯例

57.virtual关键字可以在类中进行声明:可以在基类、可以在派生类,也可以都声明。在定义虚方法时,不使用virtual关键字

58.派生类构造函数在初始化基类私有数据时,采用的是成员初始化列表语法,将基类信息传递给基类的构造函数,然后使用构造函数体初始化派生类新增的数据项;非构造函数不能使用成员初始化列表语法,但派生类方法可以调用公有的基类方法,标准技术是使用域解析运算符来调用基类方法

59.派生类没有声明、在基类中声明的函数,可以在使用派生类时直接用,不需要域解析运算符

60.为何定义虚析构函数:假设使用基类引用来表示派生类,在释放时,如果不定义虚析构函数,则使用基类的析构函数,但如果定义虚析构函数,则首先考虑是否是基类,如果是派生类,则先调用派生类的虚析构函数,再使用基类的虚析构函数(保证析构函数的调用顺序正确)

61.如果派生类的析构函数有一定操作,则必须定义基类的虚析构函数,以保证基类指针、引用调用虚析构函数时隐式地首先调用派生类的析构函数

062-079 静态联编和动态联编

62.将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编,在C++中,由于重载,使得这个任务复杂。编译器必须查看函数参数以及函数名才能确定使用哪个函数,C/C++编译器在编译过程完成这种联编

63.①在编译过程中进行联编被称为静态联编,又称为早期联编;②但虚函数让这个工作变得困难,因为使用哪个函数在编译时不确定,所以编译器必须生成能够在程序运行时选择正确的虚方法的代码,被称为动态联编,又称为晚期联编

64.动态联编与通过指针和引用调用方法相关,这在某种程度上是由继承控制的。通常,C++不允许一种类型的地址赋给另一种类型的指针,也不允许一种类型的引用指向另一种类型。但是,指向基类的引用或指针可以引用派生类对象,不必进行显式类型转换

65.将派生类引用或指针转换为基类引用或指针被称为向上强制转换,这使公有继承不需要进行显式类型转换。同时,向上强制转换是可传递的,也就是说可以使用指向基类的引用和指针指向类的派生类的派生类

66.将基类指针或引用转换为派生类指针或引用,称为向下强制转换,如果不使用显式类型转换,这种强制类型转换是不允许的。派生类的数据成员的类成员函数不能应用于基类。即使显式向下强制转换,这种转换也是不安全的

67.对于使用基类引用或指针作为参数的函数调用,将进行向上转换。隐式向上强制转换使基类指针或引用可以指向基类对象或派生类对象,因此需要动态联编,使用虚成员函数来满足要求

68.编译器对非虚方法使用静态联编:方法非虚时,指针类型(基类指针)在编译时已知,因此编译器在编译时,可以将方法关联到对应的基类成员函数。只有在方法声明为虚方法时,编译器才在程序运行时选择对应的方法,即此时是动态联编

69.为什么有两种联编?动态联编让程序员可以重新定义类方法,静态联编的效率高且符合概念模型。动态联编需要定义很多指针,而如果类不会用作基类或派生类不重新定义类方法,则静态联编效率更高,因此它也被设置成默认联编方式

70.概念模型是指设计类时,可能包含一些不再派生类重新定义的成员函数,如果要在派生类种重新定义基类的方法,则将它设置为虚方法,否则设置为非虚方法(效率更高,不需要重新定义函数)

71.编译器处理虚函数的方法:给每个对象添加一个隐藏成员,其中保存了一个指向函数地址数组的指针,这种数组称为虚函数表(vtbl),存储了为类对象进行声明的虚函数的地址。基类对象包含一个指针,该指针指向基类种所有虚函数的地址表,派生类对象将包含一个指向独立地址表的指针,如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,如果没有虚函数,则vtbl保存函数原始版本的地址,如果派生类定义了新的虚函数,则该函数的地址也在vtbl中。且只有一个地址成员,只是表的大小不同

72.调用虚函数,程序将查看存储在对象中的vtbl地址,然后转向对应的函数地址表。使用类声明中定义的第几个虚函数,就使用数组中第几个函数地址

73.在使用虚函数时,内存和执行速度方面的成本:①每个对象都增大,增大量为存储地址的空间;②每个类编译器都创建一个虚函数地址表(数组);③每个函数调用,都需要执行一项额外操作,即到表中查找地址

74.构造函数不能是虚函数:派生类不继承基类的构造函数,所以将类构造函数声明为虚的没有意义

75.析构函数应当是虚函数,除非类不用做基类。只有这样才能在向上强制类型转换时正确地释放内存,即首先释放派生类,再释放派生类创建的基类的

76.友元不能是虚函数,因为友元不是类成员,只有成员才能是虚函数。如果出现问题,可以让友元函数使用虚成员函数解决问题

77.在基类版本没有隐藏的情况下,派生类没有重新定义函数,则将使用基类版本,派生类位于派生链中,则使用最新的虚函数版本

78.重新定义不会生成函数的两个重载版本,而是隐藏了基类的版本,即重新定义继承的方法并不是重载,也即隐藏所有的同名基类方法

79.继承方法的经验规则:①如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针,这种特性称为返回类型协变,因为允许返回类型随类类型的变化而变化;②如果基类声明被重载了,则应该在派生类中重新定义所有的基类版本。如果不需要修改,则新定义可以只调用基类版本,而如果只定义一个版本,则其他版本将无法使用

080-082 protected访问控制

80.关键字protected与private类似,在类外只能用公有类成员来访问protected部分中的类成员,区别只有在基类派生的类中才会表现出来。派生类成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员,因此,对于外部,保护成员的行为与私有成员相似,但对于派生类,保护成员的行为与公有成员相似

81.但使用protected也存在一定限制,它使得变量称为”公有变量“,可能忽略一些本来需要的功能

82.最好对类数据成员采用私有访问控制,不使用保护访问控制,同时通过基类方法使派生类能访问基类数据。对于成员函数,保护访问控制很有用,让派生类能够访问公众不能使用的内部函数

083-089 抽象基类

83.抽象基类(abstract base class,ABC):假设A类和B类具有一定共性(如圆和椭圆),则可以将A类和B类共有的抽象出来放到C类中,再将A和B类从C类中继承

84.对于抽象类中有的,在其他派生类中实现方法类似但参数不同的方法,可以声明为纯虚函数,声明方法为virtual return_type name() =0,即后面多加一个=0

85.当类声明中包含纯虚函数时,不能创建该类的对象。包含纯虚函数的类只用作基类,要成为真正的ABC,必须包含至少一个纯虚函数,C++允许纯虚函数有定义

86.可以使用ABC基类指针数组管理所有的派生类,派生类有时被称为具体类,表示这些类型可以创建对象。ABC描述的是至少使用一个纯虚函数的接口,从ABC派生出的类将根据派生类的具体特征,使用常规虚函数来实现接口

87.ABC理念:在处理继承问题之前,首先开发一个模型,指出编程问题所需的类以及它们之间相互关系。一种思想:要设计类继承层次,只能将那些不会被用作基类的类设计为具体的类

88.ABC要求器具体派生类覆盖纯虚函数,迫使派生类遵循ABC设置的接口规则,这样确保了从ABC派生的所有组件都至少支持ABC指定的功能

89.如果ABC类函数与程序员定义的同名函数发生冲突,可以:①将这些函数声明为静态的;②把这些函数放在一个独立的命名空间中;③将这些结构和函数放在类定义的保护部分

090-094 继承和动态内存分配

90.派生类不使用new:派生类不需要析构函数,派生类的默认构造函数在执行自身代码后调用基类析构函数,所以可以使用派生类的默认析构函数;由于派生类不使用new,则数据成员无对象,所以可以使用默认复制构造函数和默认重载赋值运算符

91.派生类使用new:必须为派生类定义显式析构函数、复制构造函数和赋值运算符。派生类的析构函数必须释放派生类指针管理的内存,但是不用管基类的(自动调用基类析构函数);派生类的复制构造函数只能访问自己的数据,因此它必须调用基类的复制构造函数处理共享的基类数据,使用成员初始化列表完成;派生类通过显式调用基类赋值运算符来完成对派生类对象的赋值

92.在使用赋值运算符时,派生类应当使用函数表示法:baseClass::operator=(derivedClass);,这里等同于*this=derivedClass,形成正确的递归调用,使得赋值运算符被正确调用

93.总结:对于析构函数,自动完成;对于构造函数,通过在初始化成员列表中调用基类的复制构造函数完成,如果不这样,将自动调用基类的默认构造函数;对于赋值运算符,通过使用作用域解析运算符显式地调用基类的赋值运算符完成

94.派生类使用基类的友元:派生类的友元可以访问派生类数据成员,可以使用基类的友元函数访问基类的数据成员;友元不是成员函数,不能使用域解析运算符指出使用哪个函数,因此使用强制类型转换,匹配原型时可以选择正确的函数

095-111 类设计总结

95.默认构造函数:要么没有参数,要么所有的参数都有默认值。如果派生类构造函数的成员初始化列表中没有显式调用基类构造函数,则编译器将使用基类的默认构造函数来构造派生类对象的基类部分,这时如果基类没有构造函数,将导致编译阶段错误;如果类包含指针成员,则必须初始化这些成员,因此最好提供一个显式默认构造函数,将所有的类数据成员都初始化为合理的值

96.复制构造函数:接受其所属类的对象作为参数,使用复制构造函数的情况:①将新对象初始化为另一个同类对象;②按值将对象传递给函数;③函数按值返回对象;④编译器生成临时对象。如果程序没有使用复制构造函数,编译器提供原型,但不定义;使用new初始化的成员指针通常要求执行深复制,或者类包含需要修改的静态变量,这是需要定义自己的复制构造函数

97.赋值运算符:用于处理同类对象之间的赋值,与初始化不同,语句创建新对象,则使用初始化,如果修改已有对象的值,则是赋值;默认赋值是成员赋值,如果类成员是类对象,则默认成员赋值将使用相应类的赋值运算符;需要显式复制构造函数则需要显式赋值运算符

98.构造函数:构造函数不同于其他类方法,因为它创建新的对象,而其他类方法只是被现有的对象调用。构造函数不能被继承,继承意味着派生类对象可以使用基类方法,但是构造函数在完成工作之前,对象并不存在

99.析构函数:一定要定义显式析构函数来释放类构造函数使用new分配的所有内存,并完成类对象所需要的任何特殊的清理工作;对于基类,即使它不需要析构函数,也应当提供一个虚析构函数

100.转换:使用一个参数就可以调用的构造函数定义了从参数类型到类类型的转换(可以有其他默认参数),将可转换的类型传递给以类为参数的函数时,将调用转换构造函数。在带一个参数的构造函数原型中使用explicit将禁止隐式转换,但允许显式转换;将类对象转换为其他类型,要定义转换函数,可以是没有参数的类成员函数,也可以是返回类型被声明为目标类型的类成员函数,函数应返回所需的转换值;包含转换函数可能使代码有二义性

101.按值传递对象与传递引用:使用对象作为参数的函数,应按引用而不是按值来传递对象,按值传递对象涉及生成临时拷贝,调用复制构造函数,调用析构函数;如果函数不修改对象,应当将参数声明为const引用;按引用传递对象,还因为在继承使用虚函数时,被定义为接受基类引用参数的函数可以接受派生类

102.返回对象和返回引用:在编码方面,返回对象和返回引用的区别只在&符号,应返回引用而不返回对象的原因是,返回对象设计生成返回对象的临时副本,这是调用函数的程序可以使用的副本;返回引用与按引用传递对象相似,调用和被调用的函数对同一个对象进行操作;★★★★★函数不能返回在函数中创建的临时对象的引用,因为当函数结束时,临时对象消失,引用非法,这是应返回对象

103.使用const:使用const可以确保方法不修改参数(在参数前用const)、确保方法不修改调用它的对象(在参数列表括号后用const)、确保引用或指针返回的只不能用于修改对象中的数据(在最前面用const)

104.is-a关系:如果派生类不是一种特殊的基类,则不要使用公有派生,使用方法实现就可以。最好的方法可能是创建包含纯虚函数的抽象数据类,并从它派生出其他类。is-a关系指出,无需进行显式类型转换,基类指针就可以指向派生类对象,基类引用可以引用派生类对象,反过来不行

105.什么不能被继承:构造函数、析构函数和赋值运算符不能被继承

106.赋值运算符:如果类构造函数使用new来初始化指针,则需要提供一个显式赋值运算符,对于派生对象的基类部分,C++将使用基类的赋值运算符,不需要为派生类重新定义赋值运算符,除非有特别添加的数据成员。如果派生类使用了new,则必须提供显式赋值运算符,必须给类的每个成员提供赋值运算符。将派生类对象赋给基类,赋值语句转换为object1.operator=(object2);;赋值运算符只处理基类成员,忽略派生类的新数据成员;将基类赋给派生类,必须有转换构造函数derivedClass(const baseClass &);另一个方法是,定义一个将基类赋给派生类的赋值运算符

107.私有成员与保护成员:对派生类,保护成员类似于公有成员;对外部,保护成员类似于私有成员。将基类成员设置为私有的可以提高安全性,将它们设置为保护成员可以简化代码编写工作,提高访问速度

108.虚方法:如果希望派生类能重新定义方法,则应该在基类中将方法定义为虚的,这样可以启动晚期联编。同时,在使用虚函数时,应当传递派生类的引用,这样才会调用派生类的方法

109.析构函数:基类的析构函数应当是虚的,这样,通过指向对象的基类指针或引用删除派生对象时,程序首先调用派生类的析构函数,再调用基类的析构函数

110.友元函数:友元函数不是类成员,不可继承。可以通过强制类型转换,将派生引用或指针转换为基类引用或指针,然后使用转换后的指针或引用来调用基类的友元函数,也可以使用dynamic_cast<>来进行强制类型转换os<<dynamic_cast<const xxx &> (name);

111.有关使用基类方法的说明:公有方式派生的类的对象可以通过多种方式使用基类的方法:①派生类对象自动继承而来的基类方法;②派生类的构造函数自动调用基类的构造函数;③派生类的构造函数自动调用基类的默认构造函数,如果没有在成员初始化列表中指定其他构造函数;④派生类构造函数显式地调用成员初始化列表中指定的基类构造函数;⑤派生类方法可以使用作用域解析运算符来调用公有的和受保护的基类方法;⑥派生类的友元函数可以通过强制类型转换,将派生类引用或指针转换为基类引用或指针,然后用该引用或指针来调用基类的友元函数

在这里插入图片描述

下期预告:包含对象成员的类、私有继承、多重继承、类模板、友元类、嵌套类

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值