《C++ Primer 第五版》学习笔记

以下为 Chapter1、Chapter2

C++是如何工作的

源文件-> 编译器-> 链接-> 可执行程序

每一个源文件在编译后都会生成.obj文件 ,通过链接就可以将所有obj文件生成一个可执行文件。

例如#include <iostream>#符号后面跟的都是预处理语句。

预处理语句是在实际编译之前处理。

#include 实际上就是寻找一个文件,将此文件内容拷贝到现在文件中,这就是 头文件

项目中的每个cpp文件都会被编译,但头文件不会被编译。编译器会给每个cpp文件生成对应的obj文件,链接器将他们合并成一个exe文件。


C++编译器是如何工作的

预处理阶段编译器会遍历所有的预处理语句并对其处理,常用的预处理语句有include if ifdef pragma

.obj文件是二进制的机器码,如果我们想查看对应的汇编代码,可以在VS的项目属性里,C/C++ --> Output File --> Assembler Output --> Assembly-Only Listing


C++链接器是如何工作的

编译器可以对不同 .cpp 文件生成对应 .obj 文件,而链接器需要将这些 .obj 问价链接起来,并确定入口程序(默认入口程序是main函数),最终生成 .exe 可执行程序。

当我们遇到 为解决的外部符号 错误时,就是链接器找不到需要的东西。

未调用的函数,链接器不会调用


复合类型:引用

引用:为对象起的别名。

示例代码

1
2
3
4
5
6
7
8
9
10
11
// 引用
#include <iostream>
int main()
{
int a = 10;
int &b = a;
b = 8;
std::cout << "a: " << a << std::endl
<< "b: " << b << std::endl;
return 0;
}

代码输出

1
2
a: 8
b: 8

例如上面代码,其中 b 是 a 的引用,相当于给变量 a 重新起了一个名字 b 。

&b 必须初始化,而且后续不能修改

  • 定义引用时,把引用和他的初始值绑定在一起,而不是将初始值拷贝给使用。
  • 引用本身不是对象,所以不能定义引用的引用。


复合类型:指针

对地址的封装,本身就是一个对象

  • **定义指针 int *p,p为一个指针对象。**

  • 可以使用取地址符(&) 获取指针所封装的地址。

  • **可以使用解引用符(*)利用指针访问对象,示例如下**

示例代码

1
2
3
4
5
6
7
8
9
10
#include <iostream>
int main()
{
int a = 100;
int *p = &a;
std::cout << "p: \t" << p << std::endl
<< "&p: \t" << &p << std::endl
<< "*p: \t" << *p << std::endl;
return 0;
}

示例输出

1
2
3
4
5
6
// 变量p里面存放的数据
p: 0x60fe7c
// 变量p在内存中的地址
&p: 0x60fe78
// 变量p里面存放数据对应地址的数据
*p: 100


复合类型:空指针

初始化

  • C++11: int* p = nullptr;

  • int* p = 0;

  • int* p = NULL; // 需引入cstdlib

void* 指针

纯粹的地址封装,与类型无关。可以用于存放任意对象的地址。一般用在返回值类型。

指向指针的指针

如下图,ppi -> pi -> ival

其中 ppi 存放 pi 的地址,pi 存放 ival 的地址, ival 存放数字1024。

20221104_1

指针的引用

指针是对象,可以引用

例如:

1
2
3
4
5
6
int i = 1024;
int *p;
int *&r = p; // r是p的一个别名

r = &i; // r或者p存放的i的地址,就是说p指向了i,而r又是p的一个别名。
*r = 0// 对r解引用赋值,相当于i=0;


const 限定符

  • 使用const对变量的类型加以限定,变量的值不能被改变。
  • const对象必须初始化(其他时候不饿能出现在等号左边)
  • 默认状态下,const对象仅在文件内有效,如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。

例:

1
2
3
4
5
6
7
8
9
10
11
12
// 0204-1.cpp
extern int a = 100;

// 0204.cpp
#include <iostream>

int main(){

extern int a;
std::cout<<a<<std::endl;
return 0;
}

编译:g++ 0204.cpp 0204-1.cpp

这样是可以成功运行并输出a的值的。

const指针

  • 指针是对象,也可以限定为常量(必须初始化)。

  • **把*放在 const 之前,说明指针是一个常量。不变的是针织本身的值,而非指向的那个值。**

  • 顶层const:便是变量本身是一个常量。

  • 底层const:表示指针所指向的对象是一个const。

常量表达式(const expression)

指不会改变并且在编译过程中就能得到计算结果的表达式。

例如:const int maxSize = 100;

constexpr

  • C++11,允许将变量声明为constexptr类型,以便由编译器验证变量的值是否是一个常量表达式。
  • 一定是常量
  • 必须用常量表达式初始化
  • 例如:constexpr int maxSize = 100;或指针类型constexpr int* p = nullptr;
  • 自定义类型、IO库、string等类型不能被定义为constexpr。


typedef

为了提高可读性,给类型起个别名。

20221104_2

auto

C++11,让编译器通过初始值推断变量类型。

decltype类型说明符

  • 选择并返回操作数的数据类型。
  • 只要数据类型,不要值。

例如:decltype (f()) sum = x; 含义为:拿出函数 f() 的返回值类型,定义sum 并赋值为 x;

自定义数据结构

类定义:可以使用class或struct。

区别: struct是public的,class是private的。

编写自己的头文件

预处理变量有两个状态:已定义、未定义

1
2
3
4
5
6
7
8
9
#ifndef SALES_DATA_H
#define SALES_DATA_H

#include <string>
struct SaleData{
std::string name;
}

#endif

如果没有 ifndef 、define 、endif 在预处理时会出现重复处理导致运行出错。