【C++ 系列笔记】01 C++ 与 C
C++ 与 C
Hello world!
#include <iostream>
using namespace std;
int main() {
cout << "Hello world!" << endl;
system("pause");
return EXIT_SUCCESS;
}
命名空间(作用域)
-
双冒号
::
作用域运算符{$namespace}::
-
如果不写 namespace 则使用全局命名空间
::function(params);
-
-
命名空间:定义在全局命名空间下
namespace {$namespace}{ // ...函数 类 结构体 变量... }
-
命名空间可嵌套
namespace {$namespace}{ // ...函数 类 结构体 变量... namespace {$namespace}{ // ...函数 类 结构体 变量... } }
-
多处同样的命名空间会累加而不会覆盖
namespace {$namespace}{ int a; // ... } namespace {$namespace}{ int b; // ... } // a b 均在 {$namespace} 中
-
无名命名空间仅在当前文件中可用
namespace { int a; // 相当于 static int a; // ... }
-
命名空间的别名
namespace anotherName = {$namespace};
C → C++ 异同
-
变量检测
- c success cpp failure
int a; int a = 1; // c success // cpp failure
- cpp success
int a = 1; // cpp success
-
函数检测
- c success cpp failure
int function(p){// 形参类型 // 返回值 } // c success // cpp failure
- cpp success
int function(int p){// 形参类型 return 0;// 返回值 } // cpp success
-
函数调用检测
- c success cpp failure
int function(p){ } int main(){ function(1, 2, 3);// 参数数量 } // c success // cpp failure
- cpp success
int function(int p){ return 0; } int main(){ function(1, 2);// 参数数量 } // cpp success
-
类型转换
- c success cpp failure
char* p = malloc(4);// 隐式转换 // c success // cpp failure
- cpp success
char* p = (char*) malloc(4);// 隐式转换 // cpp success
-
结构体
- c failure cpp success
struct Type{ void method(); } // c failure // cpp success
-
三目运算符
- c failure cpp success
int a = 1, b = 2; (a > b ? a : b) = 3; // C 报错,认为 = 左边不是一个左值 // C 的三目返回一个值,而 C++ 返回引用
- c success
*(a > b ? &a : &b) = 3;
-
const 常量
-
C 的 const 是伪常量,C++ 不是
const int a = 1; int* p = (int*)&a; *p = 2; // C 伪常量,可以修改,但在全局作用域声明的常量不可修改
-
c
*p = 2 // p 指向 a
a = 2
-
cpp
*p = 2 // p 指向 a 的一个拷贝
a = 1
-
本质上来说,只要分配了内存,都可以通过指针去改。
而 C++ 对 const 变量(大部分情况下)通过一个 hash 表存取。
在自定义数据类型中或通过变量初始化 const 变量都会分配内存,可以直接修改。
-
-
‘C 默认认为 const 为外部变量,C++ 不会
-
引用
-
返回引用的函数(表达式)可以作为左值(容易忽略的一种用法)
-
引用的本质是一个指针常量
int* const var
,在底层实现(汇编)中与直接使用指针几乎没有任何区别 -
对数组的引用
int arr[10] = {...}; int (&arrRef)[10] = arr; // 注意 int& arrRef[10] 是引用的数组,即存放引用的数组
-
const int &ref = 10
常量引用(主要用于修饰形参)const int &ref = 10;
底层实现大概是这样的,分配了内存,可以通过指针修改
int temp = 10; int* const ref = temp;
-
C++11 的右值引用
int&& a = 10;
一定是一个右值,但允许修改
内联函数
用来代替 C 的宏
-
宏的缺陷
-
可读性
-
无类型
-
实现基于字符替换,无逻辑性
#define Max(a, n) ((a) < (b) ? (a) : (b) int main(){ int a = 10; Max(++a, 11); // 展开为 ((++a) < (11) ? (++a) : (11) // 很明显与预期不符 }
-
-
内联函数
inline void function(){ // 实现 }
内联函数实际上是代码片段,调用内联函数实际上是直接运行代码片段。
省下了函数调用的开销。
- 以下情况会导致编译器不认为这是一个内联函数(即使你加了 inline 关键字)
- 存在循环
- 存在过多的条件判断
- 函数体庞大
- 对内联函数进行取址(内联函数没有入口)
- 以下情况会导致编译器不认为这是一个内联函数(即使你加了 inline 关键字)
-
类内定义的成员函数默认是内联函数
函数的默认参数
void function(int param1 = 1, string param2 = "2"){
// 实现
}
int main(){
function();
}
-
如果函数声明存在默认参数,则函数定义(实现)必须没有
即函数声明和定义中仅允许一处存在默认参数
函数的占位参数
void function(int){
// 实现
}
主要用来重载 ++,作为占位参数重载函数。
函数重载
-
重载函数必须在同一个作用域
-
区分重载函数的条件
- 参数列表(类型、个数和顺序)
- 注意存在默认参数时的二义性
-
C++ 符号的 C 链接性
C++ 调用 以 C 的方式编译的 C 程序 就会出现链接不到函数的情况。
这是 C++ 的多态性导致的,这使得 C++ 在函数名方面 与 C 存在不同的底层实现方式。
解决方法:
-
extern "C"
告诉编译器以 C 的方式编译这个 C++ 函数(即不做 C++ 的多态性底层处理),以保证 C++ 调用的 C 链接性。extern "C" void function();
-
或者直接在 C 的头文件中全部包含
extern "C" {}
// 在 C 函数的头文件中 #ifdef __cplusplus exteern "C"{ #endif // 函数声明 #ifdef __cplusplus } #endif
还存在一个问题:
C++ 直接调 C 函数,VC++ 编译出错,g++ 编译没问题。
猜测 g++ 偷偷做了优化。
-