前言
C语言和C++是两种密切相关但又有明显区别的编程语言。C++是在C语言的基础上发展起来的,它增加了面向对象编程(OOP)的特性,同时保留了C语言的过程化编程能力。
C++发展
**C++**是一种多功能的编程语言,它起源于1980年代初,由Bjarne Stroustrup在C语言的基础上发展而来。C++最初被设计为支持面向对象编程的C语言变体,后来经过多次迭代和标准化,发展成为一种广泛应用于系统软件、游戏开发、嵌入式系统等领域的语言。C++的发展经历了多个重要阶段,包括C++98、C++03、C++11、C++14、C++17、C++20和即将到来的C++23标准.
标准输出流(cout)
cout
是一个对象,用于向标准输出设备打印数据。使用 <<
(插入操作符)向 cout
发送数据。
标准输入流(cin)
cin
是一个对象,用于从标准输入设备读取数据。使用 >>
(提取操作符)从 cin
提取数据。
二、C++命名空间
命名空间(namespace)是C++中用于组织代码和避免命名冲突的一种机制。
- 通过将一组相关的名称封装在一起,命名空间允许开发者在不同的代码库之间使用相同的名称而不会产生冲突。
- 这对于大型项目或者包含多个库的项目尤为重要,因为它有助于保持代码的清晰性和模块化,使得维护和协作更加容易.
如何定义以及使用
命名空间的定义
命名空间的声明使用namespace关键字后跟命名空间的名称,并通过一对花括号{}包围命名空间内的所有元素。
namespace mynamespace { int value = 10; }
命名空间的使用
- 直接使用命名空间和作用域解析运算符:
int result = MyNamespace::value;
- 使用using声明引入命名空间中的单个实体:
using MyNamespace::function; function();
- 使用using namespace声明引入整个命名空间
using namespace MyNamespace; int result = value;
域作用限定符( :: )
- 全局作用域符:使用
::
符号,用于指定某个标识符属于全局命名空间。即使在局部作用域中存在同名的变量或函数,全局作用域符也可以用来引用全局命名空间中的实体。 - 类作用域符:结合类名和
::
符号,用于指定某个标识符属于特定类的作用域。这种限定符常用于解决类内部成员与其他同名实体之间的命名冲突。 - 命名空间作用域符:结合命名空间名和
::
符号,用于指定某个标识符属于特定命名空间的作用域。这有助于管理大型项目中的命名冲突,并提高代码的可读性。
示例:
#include<iostream> using namespace std; namespace mynamespace { int a = 3; } int a = 2; int main() { int a = 1; cout << a << endl; cout << ::a << endl; cout << mynamespace::a << endl; return 0; }
这个示例会一次会在屏幕上依次打印 1 2 3。 分别是局部域,全局域,命名空间域。
三、缺省参数
缺省参数是一种便捷的机制,允许在函数声明时为参数指定默认值。如果在调用函数时省略了这些参数,编译器会自动使用它们的缺省值。使用缺省参数可以简化函数调用,减少冗余代码,并提高代码的可读性和灵活性。
- 全缺省参数:函数的所有参数都可以有缺省值,调用时可以不传递任何参数。
- 半缺省参数:只有部分参数有缺省值,这些缺省值必须从右向左依次指定,不能间断。
- 声明与定义分离:如果函数声明和定义分开,缺省参数只能在声明中指定,定义中不应再次指定。
- 缺省值的限制:缺省值必须是常量表达式或全局变量,不能是局部变量或动态分配的内存。
- 调用顺序:实参的传递顺序是从左向右,而形参的缺省值赋值顺序是从右向左。
#include <iostream> using namespace std; void func1(int a, int b = 10) { cout << a + b << endl; } void func2(int a, int b = 10) { cout << a + b << endl; } int main() { //确省参数 func1(1); //没有缺省 func2(1, 2); return 0; }
- func1函数调用得到11。(缺省)
- funct2函数调用得到3。(未确省)
四、函数重载
函数重载是C++中的一项特性,它允许在同一作用域内声明具有相同名称但参数列表不同的函数。
函数重载的规则
- 函数名称必须相同。
- 参数列表必须不同,这包括参数的个数、类型或顺序的不同。
- 返回类型不影响函数重载,即使两个函数的返回类型不同,它们也可以构成重载,因为重载决策是基于函数的参数列表进行的.
示例:
void ADD(int a, int b) { cout << a + b << endl; } void ADD(double a, int b) { cout << a + b << endl; } void ADD(int a, double b) { cout << a + b << endl; } int main() { ADD(1, 1); ADD(1, 1.1); ADD(1.1, 1); return 0; }
探讨C++为什么支持函数重载,C语言不支持
- 函数重载是通过编译器在编译时期根据函数的签名(即函数名和参数列表)来区分不同的函数实现的。
- 当编译器遇到一个函数调用时,它会根据提供的实参类型和数量来决定调用哪个重载版本的函数。
- 这个过程称为名称修饰(Name Mangling),编译器会为每个重载的函数生成一个独特的内部名称,即使它们的返回类型不同,只要签名不同,编译器就能正确地解析函数调用。
void Stackpush(int* a, int b) { } void Stackpush(int* a, double b) {} int mian() { int a[10] = {}; Stackpush(a,1); Stackpush(a,1.2); return 0; }
在调用这个函数的时候下是汇编语言在看到红色方框框起是进行函数修饰的名称,明显的是上面的名称不一样。
五、引用
引用的概念
在C++中,引用是一种特殊的变量类型,它提供了对另一个变量的别名。
- 引用在语法层面是不开辟空间的
- 在底层引用类似于指针变量是开辟空间
这段代码打印出原来的地址是一样的
void TestRef() { int a = 10; int& ra = a;//<====定义引用类型 printf("%p\n", &a); printf("%p\n", &ra); }
引用的应用场景
作为函数参数
上面说引用类似于指针是可以作为参数使用的
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }
作为返回值
简而言之,就是引用作为函数返回值,没什么过多的可以讲的。举一个易出错的例子:
反例:
int& Add(int a, int b) { int c = a + b; return c; } int main() { int& ret = Add(1, 2); Add(3, 4); cout << "Add(1, 2) is :"<< ret <<endl; return 0; }
这个代码将C的值返回,返回值是别名,当调用函数的时候,函数会建立栈帧,当调用结束的时候,这空间的随之就会返回给操作系统,C变量也就不会存在。输出是随机值。
六、auto关键字
auto
关键字在C++中主要用于类型推导,它允许编译器根据变量的初始化表达式自动推断变量的类型。
- C++14允许使用decltype(auto)简化写法
- C++14允许在函数定义时使用auto
- C++11引入自动类型推导
auto类型在创建的时候必须初始化
例如:
int a = 0; auto b = a; //这两句代码是打印类型 cout << typeid(a).name() << endl;//结果是 int cout << typeid(b).name() << endl;//结果是 int
应用更多的场景是面对一些例如:
std::map<std::string, std::string> dict; //std::map<std::string, std::string>::iterator dit = dict.begin(); // 等价于 auto dit = dict.begin();
七、内联函数(关键字:inline)
内联函数是C++中的一种优化技术,旨在通过将函数体直接插入到每个调用点来减少函数调用的开销。
最佳适用规则:
- 将小型、简单且频繁调用的函数声明为内联。
- 避免在类外部定义内联函数,除非确实需要。
- 对于复杂的函数或可能不被编译器内联的函数,不要使用
inline
关键字。 - 考虑编译器的实现细节和优化策略,合理使用内联函数以平衡性能和代码体积。
编译器的决策
在函数声明的时候加入关键字 inline 仅仅是程序员像使用内联函数给编译器一个建议,编译器是否使用就是根据综合考虑。编译器会根据函数的大小、复杂性、调用频率以及其他优化策略来决
定是否进行内联。因此,即使声明了inline
,编译器也可能选择将函数当作普通函数来处理.
- 避免在类外部定义内联函数,除非确实需要。
- 对于复杂的函数或可能不被编译器内联的函数,不要使用
inline
关键字。
- 考虑编译器的实现细节和优化策略,合理使用内联函数以平衡性能和代码体积。
编译器的决策
在函数声明的时候加入关键字 inline 仅仅是程序员像使用内联函数给编译器一个建议,编译器是否使用就是根据综合考虑。编译器会根据函数的大小、复杂性、调用频率以及其他优化策略来决定是否进行内联。因此,即使声明了inline,编译器也可能选择将函数当作普通函数来处理.