前言:终于到了C++章节,已经有点迫不及待了。今天我们来学习C++入门小知识。
1. 命名空间
1.1 namespace的价值
在C/C++中 ,变量,函数名以及后面学习的类都是大量存在的,这些变量,函数名和类的名称都存在于全局作用域中,可能会导致很多冲突 。使用命名空间的目的就是为了解决命名冲突的问题。namespace是一个关键字。
c
#include<stdio.h>
#include<stdlib.h>
int rand = 20;
int main()
{
//重定义,rand是一个函数,包含在stdlib.h里
printf("%d\n", rand);
return 0;
}

我们本意是想要打印整型变量的,但在C++里不同,编译器会在 局部域先进行查找,找不到再去全局域查找。此时全局域有两个rand,一个是rand函数,一个是rand整型变量,这时就会发生报错。
1.2 namespace的定义
. 定义命名空间,需要使用namespace关键字,后面跟命名空间的名字,然后接一对{}即可。{}中即为命名空间的成员。命名空间中可以定义变量,函数,类型等。
c
//解决了命名冲突的问题
namespace LC
{
int rand = 20;
struct Node
{
int val;
struct Node* next;
};
}
int main()
{
//这里默认访问的是全局域里的rand函数指针
printf("%p\n", rand);
//指定了rand的出处,就会直接在LC命名空间里去查找
printf("%d\n", LC::rand);
return 0;
}
. namespace本质上是定义出了一个域,这个域跟全局域各自独立,不同的域可以定义同名变量。
. C++中有局部域,全局域,命名空间域,类域 ;域影响的是编译器语法查找变量/函数/类型出处的逻辑,所以有了域隔离,名字冲突的问题就解决了。局部域和全局域除了会影响编译器的查找逻辑,还会影响变量的生命周期,命名空间域和类域不会影响变量的生命周期。
. namespace只能定义在全局,也可以嵌套定义。
c
int main()
{
//命名空间不能定义在局部域
namespace LC
{
int a = 10;
int Add(int left, int right)
{
return left + right;
}
}
return 0;
}

. 项目工程中多文件中定义的同名namespace会认为是同一个namespace,不会发生冲突。
. C++标准库都放在一个叫std(standard)的命名空间中。
c
//命名空间的嵌套定义
namespace LC
{
namespace NSY
{
int rand = 100;
double Add(double left, double right)
{
return left + right;
}
}
int a = 10;
int Add(int left, int right)
{
return left + right;
}
}
int main()
{
printf("%d\n", LC::a);
printf("%d\n", LC::NSY::rand);
printf("%d\n", LC::Add(2, 4));
printf("%.2f\n", LC::NSY::Add(2.2, 4.4));
return 0;
}
1.3 命名空间的使用
编译器在查找一个变量的声明或者定义时 ,默认只会在局部域和全局域查找,不会到命名空间域里去查找。
c
namespace LC
{
int a = 10;
int Add(int left, int right)
{
return left + right;
}
}
int main()
{
printf("%d\n", a);
return 0;
}

所以,我们要使用命名空间里定义的变量和函数,有以下几种方式:
1.指定命名空间访问。
2.using将命名空间中的某个成员展开。
3.展开命名空间中全部成员。
c
namespace LC
{
int a = 10;
double b = 3.14;
char ch = 'a';
}
//展开命名空间部分成员
using LC::b;
//展开命名空间全部成员
using namespace LC;
int main()
{
//指定命名空间访问
printf("%d\n", LC::a);
printf("%.2f\n", b);
printf("%c\n", ch);
return 0;
}
2. C++输入&输出
1.<iostream>是Input Output Stream的缩写,是标准的输入,输出流
库,定义了标准的输入,输出对象。
2.std::cin是istream类的对象,它主要面向窄字符(narrow characters(of type char))的标准输入流。
3.std::cout是ostream类的对象,它主要面向窄字符的标准输出流。
4.std::endl是一个函数,流插入输出时,相当于一个换行字符加刷新缓冲区。
5.<<(流插入运算符),>>(流提取运算符),C语言中这两个运算符是位运算符。
6.C++的输入输出可以自动识别变量类型(本质上是通过函数重载实
现的),最重要的是C++的流能更好的支持自定义类型对象的输入输
出。
7.cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std
(standard)的命名空间中,所以要通过命名空间的使用方式去用它
们。
8.using namespace std是将标准命名空间全部成员展开(一般在日常练习中使用)
c
#include<iostream>
int main()
{
int a = 10;
double b = 3.14;
char ch = 'c';
//cout,cin是标准输入输出流的对象,属于C++标准库
//不需要像C语言使用占位符来指定打印格式,cout,cin会自动识别类型
std::cout << a << " " << b << " " << ch << std::endl;
std::cin >> a >> b >> ch;
std::cout << a << " " << b << " " << ch << std::endl;
return 0;
}

c
#include<iostream>
//将std(标准命名空间)全部成员展开
using namespace std;
int main()
{
int a = 10;
double b = 3.14;
char ch = 'c';
//不需要在指定std
cout << a << " " << b << " " << ch << endl;
cin >> a >> b >> ch;
cout << a << " " << b << " " << ch << endl;
return 0;
}
3. 缺省参数
定义 :函数声明或函数定义时为函数参数指定一个缺省值。
. 在调用函数时,如果没有指定实参,则采用形参的缺省值,否则,使用指定的实参。

. 缺省参数分为全缺省和半缺省参数。(缺省参数也可以叫做默认参数 )全缺省 就是全部形参给缺省值 ,半缺省 就是部分形参给缺省值。C++规定,半缺省参数 必须从右往左依次连续缺省,不能间隔跳跃给缺省值。

. 带缺省参数的函数调用 ,C++规定必须从左到右依次给实参,不能跳跃给实参。


. 函数声明和定义分离时 ,缺省参数不能在函数声明和函数定义中同时出现,C++规定,必须在函数声明的地方给缺省值。


4. 函数重载
定义 :C++支持在同一作用域中出现同名函数 ,但是要求这些同名函数的 形参不同,可以是参数不同也可以是类型不同。
. 形参类型不同

. 形参个数不同

. 存在歧义问题


5. 引用
5.1 引用的概念和定义
概念 :引用不是新定义一个变量,而是对已存在的变量取别名 ,编译器不会对引用变量开辟内存空间,它和它引用的变量共用同一块空间。
定义 :类型& 引用别名 = 引用对象


5.2 引用的特性
. 引用在定义时必须初始化。


. 引用一旦引用一个实体以后就不能在引用其它实体。

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

5.3 引用的使用
引用在实践中主要用于引用传参和引用作返回值,可以减少拷贝提高效率,改变引用对象的同时可以改变被引用对象。

这里传参只是传递了一个整型,拷贝并不会损失多少效率,但是如果是一个大对象呢?例如C中的结构体,拷贝就会付出很大的代价。这里暂时先了解这么多(不好举例)。
5.4 const引用
引用const对象必须用const引用,const引用也可以引用普通对象,因为对象的访问权限可以在引用过程中进行平移和缩小,但不能放大。
c
int main()
{
int a = 10;
//权限平移
int& ra = a;
//const引用也可以引用普通对象
//权限缩小
const int& rra = a;
int b = 20;
// 2 * b是一个表达式, 2 * b的结果存在临时对象中
// 临时对象具有常性,要常引用才可以
//int& rb = 2 * b;//err
const int& rb = 2 * b;
// b是int类型,rrb是double类型
//存在类型转换的问题,b会转换成double类型存放在临时对象中
//double& rrb = b;//err
const double& rrb = b;
return 0;
}
临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,C++把这个未命名对象叫做临时对象 。临时对象具有常性。
6. 指针和引用的关系
. 引用是给已存在的变量取别名,不开辟内存空间,指针存储变量的地址,要开空间。
. 引用一旦引用一个实体就不能再引用其它实体,指针可以不断的改变指向对象。
. 引用必须初始化,指针建议初始化。
. 引用可以直接访问指向对象,指针需要解引用才可以访问指向对象。
. sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(与平台大小有关)。
. 指针容易出现空指针,野指针的问题;引用相对安全一些。
7. inline函数
8. nullptr
NULL实际上是一个宏,在传统的C头文件(stddef.h)中,有如下代码:

. 在C++中,NULL被定义为字面常量0,C中被定义为无类型指针(void*)的常量。
. C++11中引入了 nullptr,nullptr是一个特殊的关键字,nullptr是一种特殊类型的字面量 ,它可以转换成 任意其他类型的指针类型。使用nullptr定义空指针可以 避免类型转换的问题,因为nullptr只能被隐式的转换为指针类型,而不能被转换为整数类型。
c
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
int main()
{
//本意是调用f(int* ptr)函数,但由于NULL被定义成0,所以调用f(int x)函数
f(NULL);
//强转
f((int*)NULL);
//nullptr能被转换成任意类型的指针类型
f(nullptr);
return 0;
}