【c++复习】C++的基础知识(常用关键字、缺省参数、函数重载、引用)

15 篇文章 0 订阅

写在开头

C++基础部分我想介绍如下几个关键点:

  • 常见关键字
  • 命名空间的定义和使用
  • 缺省参数
  • 函数重载
  • 引用、指针和引用的区别
  • 内联函数
  • 宏 ** C与C++结合
  • 空指针NULL与nullptr的区别

可以看到C++的基础部分,需要掌握的知识多为零碎细小的知识,更需要总结和整理。

C++基础

常用关键字

C语言中有32个关键字,而在C++98中,总共给出了63个关键字。所有的关键字如下所示:

asmdoifreturntrycontinue
autodoubleinlineshorttypedeffor
booldynamic_castintsignedtypeidpublic
breakelselongsizeoftypenamethrow
caseenummutablestaticunionwchar_t
catchexplicitnamespacestatic_castunsigneddefault
charexportnewstructusingfriend
classexternoperatorswitchvirtualregister
constfalseprivatetemplatevoidtrue
const_castfloatprotectedthisvolatilewhile
deletegotoreinterpret_cast

我们在此总结一些比较常用的关键字。

using namespace

命名空间使用的目的是对标识符的名称进行本地化,避免命名冲突或命名污染

定义命名空间

namespace MySpace
{

...
};

注意:

  • 命名空间可以嵌套。
  • 同一个工程可以存在多个相同名称的命名空间,编译器最终会合并成同一个。
  • 定义一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该作用域。

使用命名空间

使用命名空间有三种方法。

  1. 加命名空间名称及作用域限定符

    // namespace:MySpace
    
    int main()
    {
    	printf("%d", MySpace::a);
    }
    
  2. 使用using引入命名空间,或者引入命名空间中的某个成员

    using MySpace::a; //引入某个成员
    using MySpace; //引入整个命名空间
    

流插入和流提取操作符

  • 流插入操作符:<<,流提取操作符:>>
  • std是C++标准库的命名空间名,如果使用标准库中的函数或者其他定义,就需要使用命名空间std。
  • 使用cout标准输出对象和cin标准输入对象,需要包含头文件,并且要按照命名空间使用方法使用std。
  • cout、cin是全局的流对象,endl是特殊的c++符号,表示换行输出,都包含在中。
  • 使用C++的输入输出很方便,不需要手动控制格式,而是由操作符自动识别变量类型。
  • 流插入和流提取运算符在面对自定义类型时候,都涉及运算符重载。

内联函数(inline)

内联函数是以inline 修饰的函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数可以提升程序运行的效率。

特性:

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。缺点是可能会让目标文件变大,优点是少了函数调用的开销,提高了程序的运行效率。
  2. inline对于编译器而言只是建议,就像register 一样,不同编译器对inline实现机制可能不同。一般来说,将函数规模小(代码段短)、不是递归,且会频繁调用的函数采用inline修饰,否则会忽略inline建议。
  3. inline不建议声明和定义分离,分离会导致连接错误,因为inline被展开,就没有函数地址,链接找不到。

宏的优点:增强代码的复用性、提高性能;
宏的缺点:不方便调试(在预编译阶段进行了替换)、代码可读性差、可维护性差、容易误用,没有类型安全的检查。

C++可以替换宏的技术:

  1. 常量定义: 换用const 或者 enum
  2. 短小函数定义改用inline。

auto关键字 (c++11

随着程序的复杂程度提高,类型名也越来越复杂,所以类型将会越来越难于拼写、含义不明确导致容易出错。

早期的auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但无人使用。

c++11中,auto有了全新定义:auto作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译的时期推导得到。

auto使用:

  1. auto与指针和引用结合起来使用。

    用auto声明指针类型时,用auto和auto* 没有任何区别,但用auto声明引用类型时必须加&

  2. 可在同一行定义多个变量

    但是在同一行声明多个变量时,变量必须是相同类型,否则编译器会报错,因为编译器实际上只对第一个变量的类型进行推导,并用推导出来的类型定义其他变量。

  3. auto 也有不能使用的场景:

    • auto不能作为函数的参数
    • auto不能直接用来声明函数
    • auto旧的含义被舍弃了。
  4. auto最常见的用法就是和范围for或者lambda结合。

nullptr (c++11

指针空值:nullptr。

声明一个变量时最好给变量一个合适的初始值。我们在定义指针时,如果它没有合法的指向,我们一般会定义其为:

int* p = NULL; //c

NULL实际上是一个宏,定义如下:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针的常量。但是这些都会遇到麻烦:

void func(int)
{	cout << "func(int)" << endl; }
void func(int*)
{	cout << "func(int*)" << endl; }

int main()
{
	func(0);
	func(NULL);
	func((int*)NULL);
	return 0;
}

程序的本意是用func(NULL) 调用指针版本的func(int*) 函数,但是因为NULL 被定义成0,所以调用了func(int) 版本。

在C++98中,字面常量0既可以是整型数字,也可以是无类型指针void*常量,但是编译器默认情况下看0作为一个整型常量,所以如果要将其按照指针方式使用的话, 必须强转(void🌟)0.

这无异给我们的使用带来了困扰,所以c++11中添加了nullptr。

  1. nullptr表示指针空值,不用包含头文件,因为其是作为C++11新关键字引入的。
  2. C++11中,sizeof(nullptr) 与 sizeof((void*)0)占用的字节数相同。
  3. 后续表示指针空值,一律用nullptr。

缺省参数

缺省参数是定义或者声明函数时为函数的参数指定一个缺省值。如果在调用函数时,没有给出对应的实参,就采用该形参的缺省值,否则使用指定的实参。

void func(int a = 10)
{
	cout << a << endl;
}
int main()
{
	func(1); //传了,用自己传的
	func();  //没传,用缺省的
	
	return 0;
}

缺省函数分类:

  • 全缺省函数

    函数的所有参数都有缺省值,意味着在调用函数的时候,可以不给任何实参。上面的func就是一个例子。

  • 半缺省函数

    void func(int a, int b = 10, int c = 20){
    	cout << "a = " << a << endl;
    	...
    }
    

    上述就是半缺省函数,代表着在调用函数的时候,可以不给出部分参数的值。有注意事项:

    • 半缺省参数必须从右往左依次给出,不能间隔。
    • 缺省参数不能在函数的声明和定义中同时出现。
    • 缺省值必须是常量或者全局变量。
    • C语言不支持缺省函数。

函数重载

函数重载是函数的一种特殊情况,C++允许在同一个作用域下声明功能类似的同名函数,同名函数的形参列表(包括参数个数或者类型或者类型顺序)不同,常常用做处理实现功能类似数据类似的不同问题。接下来我们分开举例。

  • 参数类型不同

    //打算实现int类型和double类型的Add函数
    int Add(int a, int b)
    {
    	return a + b;
    }
    double Add(double a, double b)
    {
    	return a + b;
    }
    

    上述两个函数,参数的类型不同,但是函数名相同,这就构成了函数重载。

  • 参数个数不同

    void f()
    {	cout << "void f()" << endl;}
    void f(int a)
    { cout << "void f(int a)" << endl; }
    

    这就是参数个数不同而函数名相同构成的重载,如果我们调用f时不给任何参数,就会调用上面的无参版本;如果调用f时任给一个int 值,就会调用下面的带int 版本。

  • 参数类型顺序不同

    void f(char ch, int a)
    {
     cout << "void f(char ch, int a)" << endl;
    }
    void f(int a, char ch)
    {
     cout << "void f(int a, char ch)" << endl;
    }
    

    参数的类型顺序如果稍有不同,也可以构成重载。

C不支持函数重载,而C++支持,其原因就是:C++支持函数重载的原理是 名字修饰 (name mangling)

C/C++中程序的运行需要经过几个步骤:预处理、编译、汇编、链接。

举个例子,现在有a.cc 、b.cc两个文件,在 b.cc中定义了Add函数,而在 a.cc中使用了Add函数。
编译后链接之前,a.o 的目标文件中没有Add的函数地址,因为Add是在b.cc中定义的,所以Add的地址在b.o 中。
在链接时,链接器看到a.o 调用Add,但是没有Add的地址,就会到b.o 的符号表中找Add的地址,然后链接到一起。

链接时,面对Add函数,链接器会使用一个名字去找。每个编译器的函数名修饰规则不同,gcc的函数名修饰过后不变(意味着不能同时出现两个同名函数),g++函数名修饰过后,变成了 _z + 函数长度 + 函数名 + 类型首字母,编译器将函数参数类型信息都添加到修改后的名字中。

正因如此:C语言无法支持函数重载,因为同名函数无法区分;而C++时通过函数修饰规则来区分,只要参数不同(参数类型,参数个数,参数类型顺序),修饰出来的名字就不同,所以支持重载。

如果两个函数的函数名和参数相同,而返回值不同,不构成重载,因为调用时编译器无法区分二者。编译器区分规则是:_z + 函数长度 + 函数名 + 类型首字母

引用

引用不是新定义一个变量,而是给已经存在的变量取别名。变量和它的引用共用同一块内存空间。

引用特性:

  1. 引用在定义时必须初始化。

  2. 一个变量可以有多个引用。

  3. 引用一旦有了实体,就不能引用其他实体。

  4. 常引用: const 类型的对象,需要用常引用,即const + 类型+ & + 别名 = 实体。

    const int a = 10;
    int & ra = a; //编译时报错,因为a 是常量,如果不加const,可能会修改。
    const int& ra = a; //正确写法
    
    float b = 3.14;
    double& rb = b; //错误,类型变了。
    const double& rb = b; //可以,表示别名rb不能修改b的值。
    

使用场景:

  1. 做参数

    void Swap(int& a, int& b)
    {
    	int temp = a;
    	a = b;
    	b = temp;
    }
    

    只有当a 和b 是引用时,才奏效,就跟我们当时C语言传指针一样。

  2. 做返回值

    int& Count()
    {
    	static int n = 0;
    	n++;
    	
    	return n;
    }
    

    这里的n 是static静态的,所以可以用传引用返回,因为出了函数的作用域,n还存在。如果我们定义一个普通的对象,如下:

    int& count()
    {
    	int n = 0;
    	n ++;
    	
    	return n;
    }
    

    可以发现,出了函数的作用域,返回的对象已经销毁了,这时候必须使用传值返回。

传值和传引用的区别:

传值传参或者传值返回,函数不会直接传递参数或者将变量直接返回,而是传递这个参数或者返回值的临时拷贝,因此这样效率非常低,尤其是当参数或者返回值类型大的时候,效率更低。

引用和指针的区别:

  1. 语法概念上:引用就是别名,没有独立空间,和引用实体共用一块空间。
  2. 底层实现上:实际上是由空间的,因为引用是按照指针方式实现的。
  • 引用概念上定义一个变量的别名,而指针存储一个变量的地址。
  • 引用在定义时必须初始化,指针没有要求。
  • 引用在初始化引用一个实体之后,不能引用其他实体;指针可以在任意时刻指向同类型实体。
  • 没有NULL引用,但是有NULL指针。
  • 在sizeof中含义不同,sizeof引用结果是引用类型的大小,指针始终是地址空间所占的字节。
  • 引用++是引用实体++,指针++是指针向后偏移一个类型的大小。
  • 有多级指针,没多级引用。
  • 访问实体的方式不同,指针需要自己显式解引用,引用则是编译器自己处理。
  • 引用比指针使用相对更安全。

以上就是C++入门时需要知道的基础知识。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值