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

001-002 过程性编程和面向对象编程

1.OOP的特性:①抽象;②封装和数据隐藏;③多态;④继承;⑤代码的可重用性

2.OOP首先从用户的角度考虑对象,来描述对象所需的数据以及描述用户与数据交互所需的操作。用户与数据交互的方式有三种:初始化、更新和报告(用户接口)。完成对接口的描述,需要确定如何实现接口和数据存储

003-019 抽象和类

3.在C++中,用户定义类型指的是实现抽象接口的类设计,抽象在根据用户信息与用户之间的结构来表示它的计算过程中,起到了至关重要的作用

4.指定基本类型,完成了三项操作:①决定数据对象需要的内存数量;②决定如何释放内存中的位;③决定可使用数据对象执行的操作或方法

5.内置类型有关操作的信息被内置在编译器中,自定义C++类需要自己提供这些类型的信息。类是一种将抽象转换为用户定义类型的C++工具,把数据表示和操纵数据的方法合成一个整洁的包

6.类规范一般由两个部分组成:①类声明:以数据成员的方式描述数据部分,以成员函数的方式描述公有接口;②类方法定义:描述如何实现类成员函数

7.接口:一个共享框架,供两个系统交互时使用。比如人和计算机交互,人不能把想法直接传入计算机,需要程序作为接口交互。对于类,“公众”是使用类的程序,交互系统由类对象组成,接口由编写类的人提供的方法组成。接口让程序员编写的类对象交互的代码可用被程序使用

8.一般,将C++接口(类定义)放在头文件中,并将实现(类方法的代码)放在源代码文件中。常见约定:类名首字母用大写;类和结构类似,只是还包括成员函数、共有部分和私有部分等内容

9.类的声明方法是:class Name{private:xxxxxxx;public:xxxxxxx};;一般private的是变量,public的是方法。这里的class和函数模板的class不同(不能用typename代替class)。使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数(或友元函数)来访问对象的私有成员。公有成员函数是程序和对象的私有成员之间的桥梁,提供了对象和程序之间的接口。使用protected关键字防止继承类的成员

10.尽可能将公有接口和实现细节分开,公有接口表示设计的抽象组件,将实现细节放在一起并将它们与抽象分开被称为封装。隐藏数据(将数据放在类的私有部分中)是一种封装,将实现的细节隐藏在私有部分中也是一种封装,将类函数定义和类声明放在不同文件中,还是一种封装

11.隐藏数据是OOP的主要目标之一,因此数据项通常放在私有部分,组成类接口的成员函数放在公有部分。将处理不属于公有接口的实现细节的函数定义为私有函数。隐藏数据的好处是,需要更改实现数据表示或成员函数细节的方法时,可用只对细节进行修改,无需修改接口

12.不必(但最好)在类声明中使用private关键字,因为它是类对象的默认访问控制

13.类与结构的区别:类的默认访问类型是private,而结构是public。通常使用类来进行类描述,而结构只是一种纯粹的数据对象(POD结构,普通老式数据对象结构)

14.类的成员函数的实现和常规函数定义类似,都有函数头和函数体,也可以有返回类型和参数,它们两个独特的特征是:①定义成员函数时,使用作用域解析运算符::来标识函数所属的类(意味着可用将不同类的成员函数命名成一样的);②类方法可用访问类的private组件,除了友元函数外,非法访问private类编译器将报错

15.作用域解析运算符确定了方法定义对应的类的身份,标识符funcname()具有类作用域。类方法的完整名称中包含类名,这样的函数名是限定名,如果是简单的不加类名,则是非限定名,它只能在类作用域中使用

16.非公有接口的组成部分的私有成员函数,可以省去许多输入代码的工作,也可以节省空间。且可以不让每次重新输入计算代码,确保执行的计算完全相同,如果需要修改,则只需在一处修改

17.定义位于类声明中的函数都将自动成为内联函数,类声明常将短小的成员函数且常用的成员函数作为内联函数,着和在类外使用inline运算符定义内联函数是一样的。内联函数要求它在每个使用它的函数中都定义,因此最可靠简便的方法是将内联定义放在定义类的头文件中

18.在类声明中定义方法等同于用原型替换方法定义,然后在类声明的后面将定义改写为内联函数(改写规则)

19.声明类:ClassName name1,name2,…。每个类的数据成员都不同,存储的地址也不同,但是使用该类时将调用本类的数据成员。它们的成员函数执行同一个代码块,但是调用的数据不同。调用成员函数被称为发送消息,因此将同样的消息发送给两个不同的对象将调用同一个方法,但该方法被用于两个不同的对象

020-035 构造函数和析构函数

20.C++的目标是使得使用类与使用基本的内置类型尽可能相同:创建类,可以声明类变量,也可以使用new为类对象分配存储空间;可以将对象作为函数的参数和返回值,也可以将一个对象赋给另一个

21.C++的目标之一是让使用类对象像使用标准类型一样。要使类对象能像基本类型那样赋值,就需要相应的声明为public的函数,来访问隐藏数据成员。这个函数在创建对象时,自动对它进行初始化,这种函数就是特殊的成员函数,构造函数

22.构造函数与类同名,构造函数没有返回值,但没有被声明为void类型,实际上构造函数没有声明类型。语法是ClassName(params);,可以使用默认参数来赋值,减少工作量。原型位于类声明的公有部分

23.构造函数的定义也可以在类声明外去定义。不能将构造函数的类成员名称用作构造函数的参数名,因为构造函数的参数表示的不是类成员,而是赋给类成员的值。避免混乱,常用的做法是在数据成员名中使用m_前缀,另一种是在成员名中使用_后缀

24.构造函数的使用:①可以显式地调用构造函数,即ClassName xxx=ClassName(params);;②也可以隐式地调用构造函数,即ClassName name(params);;③使用new运算符,ClassName *name=new ClassName(params);

25.构造函数用来构造对象,无法使用对象来调用对象的构造函数(我不能自己造我自己)

26.当且仅当没有提供任何构造函数时,创建类时会自动调用默认构造函数,默认构造函数将不设置任何数据成员,不做任何工作。但如果提供了非默认构造函数,就必须定义默认构造函数,否则在进行默认声明时,将报错(即不能ClassName name;这样声明类),这样做的原因是禁止创建未初始化的对象

27.定义默认构造函数的方法:①为所有参数提供默认值;②通过函数重载的方法,定义一个没有任何参数的构造函数(不能同时用这两个方法,且一般为每个参数提供默认值,即要么用①,要么在②中提供隐式初始值)

28.用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数,析构函数,完成清理工作。使用new来分配内存的话,析构函数将delete释放内存,默认情况下的析构函数不用做任何事情

29.析构函数可以没有返回值和声明类型,且析构函数没有参数,其原型是:~ClassName();;一般不需要在析构函数内写什么语句,可以写一些输出语句来跟踪程序如何调用析构函数

30.编译器决定何时使用析构函数,通常不应在代码中显式调用析构函数(有例外,12章见)。如果类是静态对象,则在程序结束时自动调用析构函数;如果类是自动存储对象,则在程序执行完代码块时被调用;如果是使用new创建的,则它驻留在栈内存或自由存储区中,使用delete释放内存时,析构函数将被自动调用;使用临时变量时,程序在结束时对其自动调用析构函数

31.对于ClassName name=ClassName(params)这样的定义,编译器有两种不同的方法:①和ClassName name(params)的定义相同;②允许调用构造函数创造一个临时对象,然后复制给name,并丢弃临时对象(会多调用一遍析构函数)(对于name=ClassName(params)这种“赋值”,采用方法②)

32.在进行类对象复制时,C++将源对象的每个数据成员的内容复制到目标对象中相应的数据成员中

33.如果既可以初始化,又可以通过赋值来设置对象的值,则应该采用初始化的方式,这样不用创建临时变量,效率更高

34.C++11的列表初始化,可以使用ClassName name={params}、ClassName name{params}、ClassName name{}的方法进行初始化

35.当类方法不修改成调用对象时,应当声明为const成员函数,方法是return_type name(params) const,由于不修改,一般return_type为void,params为void

036-038 this指针

36.如果在类的成员函数调用中遇见:一个函数的返回值是一个类,而通过判断条件,可以返回两个不同的类,则其中一个类可以使用name.value的方法,返回值可以返回name,而另一个(自己的)数据则需要使用this指针,this->value,返回值是*this

37.this指针指向用来调用成员函数的对象(作为隐藏参数传递给方法),所有类方法都将this指针设置为调用它的对象的地址。每个成员函数(包括构造函数和析构函数)都有一个this只恨,this指针指向调用对象。如果要调用整个对象,则可用*this表达式

38.在函数的括号后面,使用const限定符可将this限定为const,这样讲不能使用this来修改对象的值

039-040 对象数组

39.对象也可以创建对象数组,声明方法与普通数组相同:①使用ClassName name[x];声明时,所有元素都使用默认构造函数;②使用ClassName name[x]={ClassName{params1},ClassName{params2},…};时,使用用户定义的构造函数,其中也可以使用默认的构造函数ClassName();

40.初始化对象数组的方案:使用默认构造函数创建数组元素,然后花括号中的构造函数创建临时对象,然后复制内容到相应的元素中

041-047 类作用域

41.在类中定义的名称(数据成员名和类成员函数名)的作用域都为整个类,作用域为整个类的名称只在该类中是已知的,在类外是不可知的

42.在类声明或成员函数定义中,可以使用未修饰的成员名称(未限定的名称),构造函数名称在被调用时,才能被识别,因为它的名称与类名相同。使用类成员时,要用直接成员运算符.或间接成员运算符->或作用域解析运算符::

43.在类中使用常量时,由于类只有在实例时才会有初始化,因此不可以直接在数据成员中定义常量。两种定义类中使用的常量的方法是:①在定义时使用static const修饰符;②使用枚举(而不用命名枚举)对常量进行赋值,而后使用

44.作用域内枚举:要是在程序中使用包含相同内容的枚举,则无法通过编译(发生冲突),这时可以使用enum class/struct name {params}的方式来声明,使用枚举值时,都需要使用枚举名来限定枚举量:enumName name=enumName::param

45.常规枚举可以自动转换为整型,但类内枚举不支持进行隐式类型转换,但可以使用强制类型转换

46.枚举用某种底层整型类型表示,C++11可以自己指定这种整型类型,语法是enum class : type name {params}

47.抽象数据类型(ADT)以通用的方式描述数据类型,而没有引入语言或实现细节。例如,使用栈管理自动变量:栈存储多个数据项(一个容器),可以对它执行的操作来描述

048-054 运算符重载

48.运算符重载是重载技术运用于运算符的一种多态,允许将运算符重载扩展到用户定义的类型。运算符重载的语法是:operator op(argument-list);其中,op必须是C++中已有的运算符号(甚至[]),不能使用@之类不是运算符的符号

49.编译器遇到重载的运算符时,解释为类的方法:var=class1.operator op (class2.param)

50.★★★★★★不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据

51.重载的运算符不必是成员函数,但必须至少有一个操作数是用户定义的类型(防止用户为标准类型重载运算符)(编译器并不会认为这是错的,但是确实不符合常理

52.使用运算符时不能违反运算符原来的句法规则,也就是说不可以改变运算符的操作数;也不可以改变运算符的优先级

53.不能重载以下运算符:①sizeof运算符;②.成员运算符;③.*成员指针运算符;④::作用域解析运算符;⑤?:条件运算符;⑥typeid一个RTTI运算符;⑦const_cast强制类型转换运算符;⑧dynamic_cast强制类型转换运算符;⑨reinterpret_cast强制类型转换运算符;⑩static_cast强制类型转换运算符

54.只能通过成员函数进行重载的运算符:①=赋值运算符;②()函数调用运算符;③[]下标运算符;④->通过指针访问类成员的运算符

055-066 友元(函数)

55.通常情况下,C++控制对类对象私有部分的访问,公有类方法提供唯一的访问途径,为了不让限制这么严格,可以使用友元。友元有三种:友元函数、友元类、友元成员函数

56.让函数成为类的友元,可以赋予函数与类的成员函数相同的访问权限。为类重载二元运算符时,常常需要友元。例如定义对象和常数的乘法,由于运算符从左到右的顺序,A* 2被编译成A.operator*(2),但2* A将发生错误。要解决这个问题,可以使用非成员函数,原型为Class operator * (double a,const Class & b),被解释为operator*(a,b),但问题是,非成员函数不能访问私有数据

57.使用friend ClassName operator op(params);声明友元函数,应当放在类声明中。需要注意:①虽然它在类声明中声明,但它不是成员函数,因此不能使用成员运算符调用;②虽然它不是成员函数,但它与成员函数的访问权限相同

58.在编写友元函数定义时,由于它不是成员函数,不需要ClassName::限定符,且不使用关键字friend。类的友元函数是非成员函数,其访问权限与成员函数相同

59.通过使用友元函数和类方法,可以用同一个用户接口表达两种操作,用于二元运算符重载。只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了那些函数可以访问私有数据

60.如果要为类重载运算符,并将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序

61.<<运算符,是C和C++的位运算符,将值中的位左移。ostream类对该运算符进行了重载,将其转换为一个输出工具。cout是一个“智能”的ostream对象,可以识别所有C++基本类型,是因为对于每种基本类型,ostream类声明中都包含了相应的重载的operator<<()定义

62.如果一个函数的参数涉及多个类,因为运算顺序的关系,将默认为第一个对象调用其成员函数;而如果有多个对象作为参数,则将哪个类的相应函数声明为友元函数,要具体看该函数调用了哪个对象的私有成员

63.不可以将重载的普通<<运算符和普通的cout<<运算符结合输出,因为编译器从左到右读出C++语句。cout<<x<<y意味着(cout<<x)<<y,iostream中定义,<<左边要求是一个ostream对象,且(cout<<x)返回值为一个ostream对象;所以,如果要连用,必须把重载的<<运算符的返回值设置为一个ostream对象的引用(可以是ofstream对象)

64.很多运算符都可以选择使用成员函数和非成员函数来实现运算符重载,一般来说,非成员函数应该是友元函数。对于成员函数,一个操作数通过this指针传递参数,另一个显式地传递参数;对于友元版本,两个参数都是显式地传递,但二者的效率是差不多的,应当尽量使用非成员函数,但有时成员函数是唯一的选择

65.函数rand()返回一个从0到某个值之间的随机整数。rand()函数使用一种算法用于一个初始种子来获得随机值,该随机值将用于下一次函数调用的种子。这实际上是伪随机数;srand()函数允许覆盖默认种子值,重新启动另一个随机数序列。这两个函数包含在头文件cstdlib中

66.函数time(0)返回当前时间,通常是某一个日期开始的秒数(time()接受time_t变量的地址,将时间放到该变量中,并返回它),这个函数包含在ctime头文件中

067-077 类的自动转换和强制类型转换

67.C++自动转换兼容的两个内置变量(可能会丢失精度),不自动转换不兼容的类型,但可以使用强制类型转换

68.两个有相关性的类可以进行相互转换(这样才有意义),或定义类和基本类型之间的转换,没有相关性的类可以强制类型转换(但是没有意义)

69.C++中,接受一个参数的构造函数将为类型与该参数相同的值转换为类提供蓝图,只有接受一个参数的构造函数才能作为转换函数,但如果给其他参数提供默认值,只有一个参数没有默认值,则可以进行隐式类型转换

70.C++提供了explicit限定符用于关闭隐式类型转换(防止意外的类型转换),在声明构造函数时使用。被关闭隐式转换的构造函数,仍然可以使用强制类型转换

71.二步(多步)转换:当且仅当转换的每一步都没有二义性时,才可以进行这种转换

72.构造函数只用于从某种类型到类类型的转换。要进行类类型到某种基本类型的转换,则必须使用特殊的C++运算符函数:转换函数(用户定义的强制类型转换),转换时的方式为:base_type bt_name=base_type(class_name)或base_type bt_name=(base_type) class_name;或让编译器自己决定:Class c_name(); base_type bt_name=c_name;

73.使用上面的方法的前提是,定义了相应的转换函数,否则编译器将报错。转换函数的定义:operator typeName();注意:①转换函数必须是类方法;②转换函数不能指定返回类型;③转换函数不能有参数

74.转换函数虽然没有返回值,但是也将返回所需的值。需要注意:int转换函数返回的是四舍五入之后的值(+0.5取普通int)

75.只有一个参数的类构造函数用于将类型与该参数相同的值转换为类类型;在构造函数中声明explicit可以防止隐式转换,只允许显式转换

76.被称为转换函数的特殊类成员运算符函数,用于将类对象转换为其他类型。转换函数是类成员,没有返回类型、没有参数、名为operator typeName(),其中,typeName是对象将被转换成的类型,将类对象赋给typeName变量或强制转化时,该转换函数被自动调用

77.重载运算符时,如果遇见需要类型转换的,可以使用友元函数,也可以使用类型转换。对于经常需要使用这个运算符进行计算的,使用友元函数更方便,不经常使用或不得不使用时,可以用强制类型转换(显式)

078-098 动态内存和类

78.使用new和delete可以分配和释放内存,但是在构造函数中使用new,在析构函数中使用delete释放,将带来一些问题

79.不能在类声明中初始化静态成员变量,因为声明描述如何分配内存,但并不分配内存。在初始化变量后分配内存。静态类成员可以在类声明之外使用单独的语句来进行初始化,是因为静态类成员是单独存储的,不是对象的组成部分

80.静态成员的初始化是在实现方法文件中的,而不是在头文件中的,因为头文件可能包含给多个文件,从而出现多个初始化副本,引发错误。初始化时需要用作用域解析符说明位置

81.使用const的静态成员需要在声明(头文件)中就进行赋值

82.在构造函数中使用new来分配内存时,必须在相应的析构函数中使用delete来释放内存。如果使用new[],则必须使用delete[]

83.string类对象如果直接赋值给char数组(或对象)的话,复制的是它首字符的地址。使用string作为参数传递数据时,需要使用引用

84.将一个类对象作为函数参数来传递,将导致析构函数被调用。原因是,虽然没有调用自己定义或默认的构造函数来创建这个对象,但编译器使用复制构造函数创建了对象,在删除它时仍然使用析构函数

85.使用一个对象来初始化另一个对象时(将一个对象赋值给另一个对象),编译器将自动生成一个ClassName(const ClassName &)构造函数(在没有定义的情况下),这种称为复制构造函数,因为它创建对象的一个副本

86.C++提供了以下成员函数(默认):①默认构造函数(如果没有定义构造函数);②默认析构函数(如果没有定义);③复制构造函数(如果没有定义);④赋值运算符(如果没有定义);⑤地址运算符(如果没有定义);C++11还提供了移动构造函数和移动赋值运算符

87.只可以有一个默认构造函数,带参数的构造函数也可以是默认构造函数(给所有参数都赋予默认值),如果有一个默认构造函数(没有参数),一个带参数(都赋予默认值)的构造函数,则会导致二义性

88.复制构造函数用于将一个对象复制到新创建的对象中。它用于初始化过程中(包括按值传递参数),而不是常规的赋值过程中。类的复制构造函数原型为:Class_name(const Class_name &);

89.新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用。即:①Class_name name1(name2),其中name2是一个已经存在的Class_name对象;②Class_name name1=name2;③Class_name name1=Class_name(name2);④Class_name *pname1=new Class_name(name2)

90.每当程序生成了对象副本时,编译器都将使用复制构造函数(按值传递对象或函数返回对象时)。按值传递意味着传递原始变量的一个副本;编译器生成临时对象时,也使用复制构造函数

91.默认的复制构造函数功能:逐个复制非静态成员(浅复制,即成员复制)。如果成员本身就是类对象,则将使用这个类的复制构造函数来复制成员对象,静态函数不受影响,因为它们属于整个类,不是各个对象

92.如果类中包含一个静态数据成员,它的值在新对象被创建时发生变化,则应该提供一个显式复制构造函数来处理计数问题

93.对于字符串,不能直接赋值(赋的是地址),且如果进行了普通赋值,则两个对象都指向同一个内存地址,如果其中一个调用了析构函数,内存被释放,则另一个再调用析构函数时可能出现严重的问题

94.如果要让字符串对象可以进行常规赋值,解决的办法是定义一个显式的复制构造函数(深度复制)。复制构造函数复制字符串,并将副本的地址赋给新变量

95.如果类中包含一个使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针。这被称为深度复制

96.赋值运算符:C++允许类对象赋值,这是通过自动为类重载赋值运算符实现的。原型是:Class_name & Class_name::operator=(const Class_name &);

97.赋值运算符与复制构造函数类似,隐式地对成员进行逐个复制,如果成员本身就是类对象,则程序将使用为这个类定义的复制运算符来复制该成员,静态数据成员不受影响

98.对赋值运算符进行深度复制,与复制构造函数类似,但也存在一些差别:①由于目标对象可以引用了以前分配的数据,所以函数应使用delete[]来释放这些数据;②函数应该避免将对象赋给自身,否则释放内存操作可能删除对象的内容;③函数返回一个指向调用对象的引用

099-100 念叨

99.这段时间有点咳嗽…我在武汉上学…1月9号离开的武汉…但愿没有什么事

100.勤洗手,多睡觉,每天好心情…

下期预告:String类的底层技术;构造函数中使用new的其他事项;返回对象的事项;指向对象的指针;“队列”模拟

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值