C++基础知识(一)

目录

1、内联函数

2、函数的默认参数

3、函数重载

4、函数模板

5、局部变量和全局变量

6、动态存储和静态存储

7、预处理命令

8、条件编译

9、类定义

10、对象定义

11、构造函数

12、析构函数

13、静态成员

14、友元函数

15、继承与派生类

16、多态性

17、静态联编和动态联编

18、虚函数

19、运算符重载

20、类模板

参考文献

1、内联函数

(1)内联函数(inline function):在编译时将被调函数的代码直接嵌入到主调函数中

  • 内联函数中不允许用循环语句和switch语句
  • 内联函数的声明必须出现在内联函数第一次被调用之前

2、函数的默认参数

(1)函数的默认参数(default argument):函数定义或声明时,为形参指定默认值。默认参数本质上是编译器根据函数声明或函数定义时的默认参数设置,对函数调用中没有给出的实参自动用默认值表达式“补齐”再进行编译

  • 函数声明和函数定义中仅能有一处设置默认参数
  • 设置多个默认参数时,设置的顺序是自右向左
  • 默认值可以是常量、全局变量、函数调用(调用实参必须是常量或全局变量的表达式),不可以是局部变量

3、函数重载

(1)函数重载(function overloading):同一个域用同一个函数名来定义多个函数,函数的参数列表、参数个数、参数类型可能不同

  • 重载函数的形参必须不同(个数不同或类型不同)
  • 不要将不同功能的函数声明为重载函数,以免出现调用结果的误解、混淆
  • 函数不能既为重载函数,又为有默认参数函数

4、函数模板

(1)函数模板(function template):一个独立于类型的函数,可以设计通用类型的函数

  • template<模板形参表> 返回类型 函数名(形参列表){函数体},如template<typename T> T abs(T x) { return x < 0 ? -x : x; }
  • 模板函数的真正代码是在源程序中的调用函数时产生
  • 模板形参表(template parameter list):用一对尖括号括起来的一个或多个模板形参的列表,不允许为空,形参之间以逗号分隔
    • typename 类型参数名1, typename 类型参数名2, ...
    • class 类型参数名1, class 类型参数名2, ...

5、局部变量和全局变量

(1)局部变量(local variable):在函数内部或复合语句(区域)定义的变量,又称内部变量,包括:在一个函数内部定义的变量、函数的形式参数、在某个复合语句中定义的变量

  • 在同一区域不能定义相同名字的变量
  • 在不同区域允许定义相同名字的变量,但本质上它们是不同的变量
  • 如果一个变量所处区域的子区域中有同名的变量,则该变量在子区域无效,有效的是子区域的变量,称为定义屏蔽

(2)全局变量(global variable):在源文件中,但在函数外部定义的变量

  • 在全局作用域中,变量或函数实体若使用static修饰,则该实体对于其他源文件是屏蔽的,称为私有的

6、动态存储和静态存储

(1)动态存储(dynamic storage duration):动态分配和释放存储空间,由函数调用自动分配释放,或由程序指令来人工分配释放

  • 自动存储(automatic storage):函数调用完成
  • 自由存储(free storage):程序员通过指令完成

(2)静态存储(static storage duration):对象的数据可以在程序运行期始终保持直到修改为止,或者程序结束为止
(3)变量的存储类型:自动变量(默认)、寄存器变量、静态局部变量、外部变量:

  • 自动变量:auto修饰,默认情况下,函数或复合语句中的对象(包含形参)为自动对象,其存储方式是自动存储
  • 寄存器变量:用CPU的寄存器来存放局部变量,用register修饰
  • 内部静态变量:static修饰的局部变量,作用域是定义它的函数,静态局部变量会保持其值
    • 内部函数:static修饰,在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用
  • 外部变量:extern修饰,extern声明了程序中将要用到但尚未定义的外部变量
    • 外部函数:extern修饰,该函数可被其他文件调用

7、预处理命令

(1)预处理命令

  • 预处理命令不是C++本身的组成部分,也不是C++语句,是C++标准规定的可以出现在C++源程序文件中的命令
  • "#"开头,可以放置在源程序中任何位置,有效范围是从出现位置开始到源程序文件末尾
  • 在“文件包含”预处理命令中,当#include后用双引号括起,寻找被包含文件的方式为:先在源程序所在目录中搜索,再按系统设定的标准方式搜索;当#include后用尖引号括起,寻找被包含文件的方式为:直接按系统设定的标准方式搜索

(2)宏定义

  • 宏:用一个标识符来表示一个字符文本
  • 宏替换(宏展开):预处理时,对程序中所有后续的宏名实例,预处理器都用字符文本去替换
  • 宏定义只是简单替换,不作语法检查,宏串中每一字符都是有效字符
  • 为了保证宏展开的结果符合设计本意,应在宏串或实参字串中加入必要的括号
  • 在宏定义中,"#"运算符的作用:
    • 将文本参数“字符串化”,如#define PRINT_MSG(x) printf(#x)
    • 将两个字符文本连接成一个字符文本,如#define SET1(arg) A##arg=arg  SET(1)宏替换为A1=1

8、条件编译

条件编译可以提高程序可维护性、可移植性、通用性
(1)#ifdef条件编译命令:测试条件字段是否定义,选择参与编译的程序代码

  • #ifdef 条件字段 ......//程序代码段1
  • #else .......//程序代码段2(可选)
  • #endif

(2)#if条件编译命令:根据表达式的值选择参与编译的程序代码

  • #if 常量表达式 ......//程序代码段1
  • #elseif 常量表达式2 .......//程序代码段2
  • #else  .......//程序代码段3
  • #endif

9、类定义

(1)类成员可以是数据或函数,对类成员进行访问,访问源包括:类成员、类用户

  • 类成员:类本身的成员函数
  • 类用户:类外部的使用者,包括全局函数,另一个类的成员函数等

(2)类的成员函数:可以在类中定义和声明成员函数,也可以声明在类中,定义在类外部,类外定义时要用类名::函数名进行定义
(3)类中类型声明时不会产生该成员的实体,不会分配存储空间
(4)类定义一般放在程序文件开头,或者放在头文件中被程序文件包含,此时定义是全局的
(5)类成员的访问控制:

  • public:公有的,外部可以直接访问
  • protected:保护的,外部不能直接访问,派生类可以访问
  • private:私有的,外部不能直接访问,派生类也不可以访问,如果没有声明访问控制属性,类所有成员默认为private
  • 在实际编程中,为了使程序清晰,每一种成员访问限制符在类体中应只出现一次,且按照public、protected、private的顺序

10、对象定义

(1)对象:类的实例化。定义类的类型时不分配存储空间,当定义对象时,将为其分配存储空间

  • 利用new运算符可以动态地分配对象空间,返回一个指向新对象的指针的值,即分配得到的是对象的内存单元的起始地址。类名 *对象指针变量 = new 类名
  • 在执行new运算时,如果内存不足,无法开辟所需的内存空间,C++编译器会返回一个0值指针,返回值不为0,则动态分配对象成功
  • delete运算符释放对象空间,new建立的动态对象不会自动被撤销,即使程序运行结束也是如此,必须人为使用delete撤销

(2)访问对象中的成员:类外部不能访问类任何private的成员

  • 对象名+对象成员引用运算符(.)
  • 指向对象的指针+指针成员引用运算符(->)
  • 对象的引用变量+对象成员引用运算符(.)

(3)对象赋值

  • 对象的赋值只对其中的非静态数据成员赋值
  • 若对象的数据成员中包括动态分配资源的指针,则赋值时只赋值指针值而不复制指针所指向的内容

(4)函数的参数

  • 当形参是对象时,实参要求是相同类的对象名,形参是实参对象的副本
  • 当形参是对象指针时,实参要求是同类对象的指针,在函数中若按间接引用方式修改了形参对象,本质上就是修改实参对象
  • 当形参是对象引用时,实参要求是同类的对象,函数形参实际上是实参对象的别名,为同一个对象,在函数中修改了形参对象,本质上就是修改实参对象
  • 如果不希望在函数中修改实参对象的值,函数形参可以作const限定

11、构造函数

(1)构造函数(constructor):是一种成员函数,用来在创建对象时初始化对象,为对象数据成员赋初始值,在创建对象时自动被执行

  • 构造函数一般声明为公有的(public),因为创建对象通常是在类的外部进行
  • 构造函数也可以声明为内联函数
  • 在构造函数的函数体中不仅可以对数据成员初始化,也可以包含任意其他功能的语句,但一般不提倡在构造函数中加入与初始化无关的内容
  • 数据成员被初始化的次序就是数据成员的声明次序
  • 设类A将其他类对象作为成员,则建立A类对象时,成员构造函数先执行

(2)构造函数的重载:唯一形参列表的构造函数
(3)如果构造函数含有默认参数,必须在类的内部指定,不能在类的外部指定

  • 一般地,不应该同时使用构造函数的重载和带默认参数的构造函数

(4)合成默认构造函数(synthesized default constructor):定义的类中没有显式定义任何构造函数,编译器会自动为该类生成默认构造函数

  • 如果类定义了一个带参数的构造函数,还需要无参数的构造函数,则必须自己定义它

(5)转换构造函数:实现其他类型到类类型的隐式转换,做法如下:

  • 先声明一个类
  • 在这个类中定义一个只有一个参数的构造函数,参数的类型是需要转换的数据类型,如类名(const 指定数据类型 &obj)
  • 采用转换构造函数定义对象时即进行类型转换,如类名(指定数据类型的数据对象)

(6)将构造函数声明为explicit(只能用于类内部的构造函数声明上),可以防止在需要隐式转换的上下文中使用构造函数
(7)复制构造函数:用一个已经生成的对象来初始化另一个同类的对象,有且只有一个本类类型对象的引用形参,通常使用const限定,属于浅复制,调用复制构造函数的场景:

  • 复制初始化 Point pt2=pt1,调用复制构造函数
  • 直接初始化 Point pt3(pt1),调用与实参匹配的构造函数
  • 当函数的参数为类的对象
  • 函数的返回值是类的对象

(8)合成复制构造函数(synthesized copy constructor):每个类必须有一个复制构造函数,如果类中没有定义复制构造函数,编译器就会自动合成一个
(9)深复制与浅复制:深复制指针指向是两份,浅复制是一份

12、析构函数

(1)~类名(){ 函数体 },当对象脱离其作用域时,系统会自动执行析构函数,析构函数往往做“清理善后”的工作

  • 析构函数不返回值,无返回类型,无函数参数,析构函数不能被重载,一个类可以有多个构造函数,但只能有一个析构函数

(2)何时调用析构函数

  • 对象在程序运行超出其作用域时自动撤销,撤销时自动调用该对象的析构函数
  • 如果用new运算动态建立了一个对象,则用delete运算释放该对象时,调用该对象的析构函数

(3)合成析构函数(synthesized destructor):编译器总是会为类生成一个析构函数

  • 合成析构函数按对象创建时的逆序撤销每个非静态成员
  • 合成析构函数不删除指针成员所指向的对象,需要程序员显式编写析构函数去处理

(4)何时需要编写析构函数:析构函数通常用于释放在构造函数或在对象生命期内获取的资源(如动态分配的内存)
(5)析构函数和构造函数的调用次序:先构造的后析构,后构造的先析构

13、静态成员

(1)类的静态数据成员:以关键字static开头,能够实现同类的多个对象之间数据共享

  • 非静态数据成员存在于类类型的每个对象中,静态数据成员独立于类的任何对象,在所有对象之外单独开辟空间存储
  • 声明了类而未定义对象,类的非静态成员不占存储空间,而静态成员分配存储空间
  • 访问静态成员同样遵循公有及私有访问规则
  • 静态数据成员必须在类外部定义:数据成员类型 类名::静态数据成员名 = 初始化式,类的静态数据成员在使用前必须先初始化

(2)类的静态成员函数:以关键字static开头的声明函数

  • 可以通过类作用域运算符(::)和通过对象名调用静态成员函数,不能通过类名调用类的非静态成员函数
  • 非静态成员函数有this指针,静态成员函数没有this指针,因此,静态成员函数不能访问本类中的非静态成员
  • 静态成员函数不能被声明为const
  • 类的非静态成员函数可以调用静态成员函数,但反之不能

14、友元函数

(1)友元机制(friend):允许一个类将其非公有成员的访问权授予指定的函数或类

  • 友元类(friend class)的所有成员函数都可以访问授予友元关系的那个类的非公有成员
  • 访问类非公有成员可以有两个用户:类成员和友元
  • 在一个类中的友元函数可以访问这个类中的私有成员,友元函数可以是另一个类的成员函数,称为友元成员函数

(2)友元的关系

  • 友元的关系是单向的
  • 友元的关系不能传递或继承,如果类B是类A的友元类,类C是类B的友元类,不等于类C是类A的友元类

(3)友元的作用:突破了封装原则,有助于数据共享,提高程序效率,但要在数据共享与信息隐蔽之间选择一个恰当的平衡点

15、继承与派生类

(1)继承是在一个已存在的类的基础上建立一个新的类。已存在的类称为基类(base class),又称为父类;新建立的类称为派生类(derived class),又称为子类

  • 基类是对派生类的抽象,派生类是对基类的具体化
  • 继承方式包括公有继承(public)、保护继承(protected)、私有继承(private),继承方式决定了对继承成员的访问权限,默认是private
  • 友元关系不能继承
  • 如果基类定义了静态成员,则整个继承层次中该静态成员只有一个实例
  • 可以在派生类中声明一个与基类成员同名的成员,则派生类中的新成员会覆盖基类的同名成员,就实现了修改基类成员功能的效果

(2)多重继承(multiple inheritance):一个派生类有两个及两个以上的基类

16、多态性

(1)多态性:一段程序能够处理多种类型对象的能力

  • 重载多态(函数和运算符重载)
  • 强制多态(类型强制转换):char->short->int->unsigned->long->unsigned long->float->double->long double
  • 可以在表达式中使用3种强制类型转换表达式,E表示运算表达式,T表示类型
    • static_cast<T>(E)
    • T(E)
    • (T)E
  • 类型参数化多态(模板):将类型作为函数或类的参数,避免为各种不同的数据类型编写不同的函数或类
  • 包含多态(继承及虚函数):至少含有一个虚函数的类称为多态类

17、静态联编和动态联编

(1)联编(binding):又称绑定,指将模块或函数合并在一起生成可执行代码的处理过程
(2)静态联编(static binding):在编译阶段就将函数实现和函数调用绑定,对于普通成员函数,如果通过对象调用普通成员函数,编译器直接调用该对象类型中的该函数对应的函数代码块;如果通过指针调用普通成员函数,那么编译器会直接根据指针类型调用该类型中的该函数对应的函数代码块
(3)动态联编(dynamic binding):程序运行到相应的语句才进行函数实现和函数调用的绑定,可以利用虚函数实现,对于虚函数来说,如果是通过对象调用虚函数,不会经过查虚函数表,是静态联编;如果是通过指针或引用调用虚函数,就会经过查虚函数表,是动态联编

  • 当编译器编译含有虚函数的类时,将为它建立一个虚函数表VTABLE(virtual table),相当于一个指针数组,存放每个虚函数的入口地址。编译器为该类增加一个指向虚函数表的指针,通常称为vptr
  • 当调用虚函数时,先通过vptr找到虚函数表,再找出虚函数的真正地址进行调用
  • 派生类能继承基类的虚函数表,只要和基类相同的成员函数,无论是否使用virtual声明,都会自动成为虚函数

18、虚函数

(1)虚函数:实现多态性,被virtual关键字修饰,虚函数可在基类和派生类使用相同的函数名定义函数的不同实现,从而实现“一个接口,多种方式”

  • 虚函数只能是类中的非静态成员函数
  • virtual只在类体中使用
  • 当在派生类中定义和基类相同的成员虚函数时,则派生类的这个成员函数无论是否使用virtual,它都将成为一个虚函数
  • 当用基类指针或引用对虚函数进行访问时,系统将根据运行时指针或引用所指向或引用的实际对象来确定调用对象所在类的虚函数版本

(2)何时使用虚函数:成员函数在类继承后可能被修改,或成员函数的调用是通过基类指针或引用去访问的
(3)虚析构函数:将基类的析构函数声明为虚函数,则由该基类所派生的所有类的析构函数也都自动成为虚函数

  • 派生类对象从内存中撤销时一般先调用派生类的析构函数,再调用基类的析构函数

(4)纯虚函数(pure virtual function):在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义

  • virtual 返回类型 函数名(形式参数列表)=0;即在虚函数的原型声明后加上"=0",表示纯虚函数根本没有函数体
  • 包含有纯虚函数的类称为抽象类(abstract class),抽象类只能作为基类来派生新类,又称为抽象基类(abstract base class),抽象类不能定义对象。抽象类至少含有一个虚函数,而且至少有一个虚函数是纯虚函数

19、运算符重载

(1)运算符重载:本质上是函数的重载,如<<是位运算的左移运算符,但在cout输出操作中作为流插入运算符

  • 返回类型 operator运算符号(形式参数列表){ 函数体 }
  • 前置单目运算符重载:返回类型 operator运算符号(){ 函数体 }
  • 后置单目运算符重载:返回类型 operator运算符号(int){ 函数体 }

(2)运算符重载规则:

  • 不能重载的运算符:.、.*、::、?:、sizeof
  • 运算符重载不能使用默认参数
  • 当运算符重载为成员函数时,运算符函数的形参个数比运算符规定的运算对象个数要少一个(少了的对象就是该对象本身),因为运算符函数可以用this指针隐式访问类对象的成员
  • 当运算符重载为友元函数时,运算符函数的形参个数和运算符规定的运算对象个数一致

20、类模板

(1)类模板是类的抽象,类是类模板的实例。利用类模板可以建立各种数据类型的类

  • template <模板形参表> class 类模板名 { 成员列表 },模板形参表如:<class 类型参数1, class 类型参数2, ...>,又如:<class T, int N>
  •  类模板可以方便表示数组、向量、列表、队列、栈、矩阵等数据结构时,因为这些数据结构的表示和算法不受其所包含的元素的类型的影响
  • 标准模板库STL(standard template library)是泛型编程(generic programming)思想的具体体现

(2)用类模板定义对象时:

  • 必须为模板形参显式指定类型实参,一般形式为:类模板名<类型实参表> 对象名1, 对象名2, ... ,如Point<double> m(1,2), n(3,4)
  • 必须为每个非类型形参提供常量表达式以供使用。如Sequence <class T, int N>,有Sequence <double, 5> a
  • 模板形参可以设置默认值,如:template <class T=char, int N=10>

(3)export关键字可以对于类模板定义在另一个编译单元下,实例化类模板对象,如export template<class T>class A<int> a,这样类模板的实例化与定义体可以不必放在同一个编译单元

参考文献

[1]魏英. C++程序设计. MOOC视频. 西北工业大学;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值