C++ 入门知识

一.C++关键字

C++共有63个关键字,包括C的32个关键字

二.命名空间

C/C++ 中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,避免命名冲突或名字污染,namespace 关键字的出现就是针对这种问题的。
#include <stdlib.h>
#include <stdio.h>

int rand = 10;

int main()
{
	printf("%d", rand);
	return 0;
}

就像这样,rand已经在全局中 被定义为了全局变量,但是stdlib库展开,又存在一个rand函数导致rand被重定义了。

所以C语言可能会面临自己定义的名字和库里面的名字发生冲突,或者在项目组中多个人之间定义的名字冲突,于是为了解决名字冲突问题,C++就提出了用namespace来解决。

2.1对namespace命名空间的正常定义

namespace lxl
{
    // 命名空间中可以定义变量/函数/类型
    int rand = 10;            //变量
    int Add(int left, int right)   // 函数
    {
        return left + right;
    }
    struct Node        //类型
    {
        struct Node* next;
        int val;
    };
}

2.2namespace命名空间的嵌套定义

namespace N1
{
    int a;
    int b;
    int Add(int left, int right)
    {
        return left + right;
    }
    namespace N2
    {
        int c;
        int d;
        int Sub(int left, int right)
        {
            return left - right;
        }
    }
}

2.3同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

namespace N1
{
    int Mul(int left, int right)
    {
        return left * right;
    }
}

ps:应该没人会把命名空间的名字写为namespace吧。。。

2.4对命名空间namespace的三种使用方法

命名空间的使用需要对变量加上::(域作用限定符), 加上这个符号就会先在这个符号左面的域去找,如果左面空,就代表全局。这个符号前面不能写函数名,只能在域里面找,不能在函数里面找。

比如:

/namespace lxl
{
	// 命名空间中可以定义变量/函数/类型
	int a = 0;
	int b = 1;
	int Add(int left, int right)
	{
		return left + right;
	}
	struct Node
	{
		struct Node* next;
		int val;
	};
}
int main()
{
	// 编译报错:error C2065: “a”: 未声明的标识符
	printf("%d\n", a);
	return 0;
}

(1).namespace的正常使用

int main()
{
	printf("%d\n", lxl::a);
	return 0;
}

(2).使用using将命名空间中某个成员引入

using lxl::b;
int main()
{
    printf("%d\n", lxl::a);
    printf("%d\n", b);
    return 0;    
}

(3).使用using namespace 命名空间名称 引入

using namespace lxl;
int main()
{
    printf("%d\n", lxl::a);
    printf("%d\n", b);
    Add(10, 20);
    return 0;    
}

三.C++输入&输出

1.说明:

1. 使用 cout 标准输出对象 ( 控制台 ) cin 标准输入对象 ( 键盘 ) 时,必须 包含 < iostream > 头文件
以及按命名空间使用方法使用 std
2. cout cin 是全局的流对象, endl 是特殊的 C++ 符号,表示换行输出,他们都包含在包含 <
iostream > 头文件中。
3. << 是流插入运算符, >> 是流提取运算符
4. 使用 C++ 输入输出更方便,不需要像 printf/scanf 输入输出时那样,需要手动控制格式。
C++ 的输入输出可以自动识别变量类型。
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
	cout << "Hello world!!!" << endl;
	return 0;
}
注意:早期标准库将所有功能在全局域中实现,声明在 .h 后缀的头文件中,使用时只需包含对应
头文件即可,后来将其实现在 std 命名空间下,为了和 C 头文件区分,也为了正确使用命名空间,
规定 C++ 头文件不带 .h ;旧编译器 (vc 6.0) 中还支持 <iostream.h> 格式,后续编译器已不支持,因
推荐 使用 <iostream>+std 的方式。
#include <iostream>
using namespace std;
int main()
{
   int a;
   double b;
   char c;
     
   // 可以自动识别变量的类型
   cin>>a;
   cin>>b>>c;
     
   cout<<a<<endl;
   cout<<b<<" "<<c<<endl;
   return 0;
}

2.std命名空间的使用惯例:

std C++ 标准库的命名空间,如何展开 std 使用更合理呢?

2.1 在日常练习中,建议直接using namespace std即可,这样就很方便。

2.2 using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。

四.缺省参数

4.1 缺省参数的概念

缺省参数是 声明或定义函数时 为函数的 参数指定一个缺省值 。在调用该函数时,如果没有指定实
参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 0)
{
	std::cout << a <<std::endl;
}
int main()
{
	Func(); //没有传参时,使用参数的默认值
	Func(10); //传参时,使用指定的实参
	return 0;
}

4.2缺省参数的分类

(1)全缺省参数

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

(2)半缺省参数

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

注意

1. 半缺省参数必须 从右往左依次 来给出,不能间隔着给
例如:
void Func(int a = 0, int b,int c = 20);
Func(  ,  2,   );
Func(2 ,   , 1);
2. 缺省参数不能在函数声明和定义中同时出现
例如:
//a.h 
void Func(int a = 10);
  
// a.cpp
void Func(int a = 20)
{}

// 如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该
// 用哪个缺省值。
3. 缺省值必须是常量或者全局变量
4. C 语言不支持(编译器不支持)

五.函数重载

1.函数重载概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这 些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

// 1、参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}
// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

2.C++支持函数重载的原理--名字修饰(name Mangling) 底层原理

C/C++ 中,一个程序要运行起来,需要经历以下几个阶段: 预处理、编译、汇编、链接

那么下面举个例子来讲述编译器在编译的时候是怎么识别的,其实是在编译的时候编译器对名字进行了修饰,我们在进行函数调用的时候,编译的时候会把调用函数那条指令变成call 地址。

这个call 后面的地址指向的是jump指令的地址
jump会跳转到调用函数的地址,然后建立栈帧,保存寄存器等等一系列的动作(涉及函数栈帧的创建和销毁)
C++对函数名进行了修饰,这个修饰的规则会因不同的平台又不同,VS的规则相对复杂,Linux的规则简单一点,我们先看一下Linux的规则。
通过上面我们可以看出在window中C语言 的函数修饰后名字不变。而 g++ 的函数修饰后变成【 _Z+ 函数长度 + 函数名 +类型首字母】。 Add(int x,int y)函数名修饰成_Z3Addii 。_Z是前缀,3是函数名的长度,ii是类型的首字母,编译之后就变成一串指令了,就有这个函数的地址,调用的时候就用修饰之后函数名去找,然后就找到了内个函数的地址。那么double类型修饰之后就变_Z3Adddd。所以参数的个数不同,类型不同,顺序不同都会影响修饰后名字的不同,这样就能找到想要调用的函数啦。
下图是Windows下名字的修饰规则。
通过这里就理解了 C 语言没办法支持重载,因为同名函数没办法区分。而 C++ 是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载

六.引用

6.1应用的概念

引用 不是新定义一个变量,而 是给已存在变量取了一个别名 ,编译器不会为引用变量开辟内存空
间,它和它引用的变量 共用同一块内存空间。
举个例子:
比如: 李逵 ,在家称为 " 铁牛 " ,江湖上人称 " 黑旋风 "
类型 & 引用变量名 ( 对象名 ) = 引用实体;
void TestRef()
{
    int a = 10;
    int& ra = a;//<====定义引用类型
    printf("%p\n", &a);
    printf("%p\n", &ra);
}
注意: 引用类型 必须和引用 实体 同种类型 的。

6.2 引用特性

1. 引用在 定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
void TestRef()
{
   int a = 10;
   // int& ra;   // 该条语句编译时会出错
   int& ra = a;
   int& rra = a;
   printf("%p %p %p\n", &a, &ra, &rra);  
}

6.3常引用

void TestConstRef()
{
    const int a = 10;
    //int& ra = a;   // 该语句编译时会出错,a为常量
    const int& ra = a;
    // int& b = 10; // 该语句编译时会出错,b为常量
    const int& b = 10;
    double d = 12.34;
    //int& rd = d; // 该语句编译时会出错,类型不同
    const int& rd = d;
}

6.4使用场景

1.做参数

void Swap(int& left, int& right)
{
   int temp = left;
   left = right;
   right = temp;
}

2.做返回值

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

注意:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

6.5 传值、传引用效率比较

      以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直 接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效 率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

6.6引用和指针的区别

语法概念上 引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
return 0;
}
底层实现上 实际是有空间的,因为 引用是按照指针方式来实现
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}
引用和指针的不同点 :
1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用 在定义时 必须初始化 ,指针没有要求
3. 引用 在初始化时引用一个实体后,就 不能再引用其他实体 ,而指针可以在任何时候指向任何
一个同类型实体
4. 没有 NULL 引用 ,但有 NULL 指针
5. sizeof 中含义不同 引用 结果为 引用类型的大小 ,但 指针 始终是 地址空间所占字节个数 (32
位平台下占 4 个字节 )
6. 引用自加即引用的实体增加 1 ,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同, 指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全

7.内联函数

7.1概念

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

7.2特性

1. inline 是一种 以空间换时间 的做法,如果编译器将函数当成内联函数处理,在 编译阶段,会
用函数体替换函数调用 ,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
2.   inline 对于编译器而言只是一个建议,不同编译器关于 inline 实现机制可能不同 ,一般建
议:将 函数规模较小 ( 即函数不是很长,具体没有准确的说法,取决于编译器内部实现 )
是递归、且频繁调用 的函数采用 inline 修饰,否则编译器会忽略 inline 特性。
3. inline 不建议声明和定义分离,分离会导致链接错误。因为 inline 被展开,就没有函数地址
了,链接就会找不到。
  • 46
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值