# C++知识点

继续学习：

类详解：<http://www.runoob.com/cplusplus/cpp-classes-objects.html>

指针详解：<http://www.runoob.com/cplusplus/cpp-pointers.html>

## 1. 定义常量#define和const的区别

| #define              | Const    | 备注                                                                                      |
| -------------------- | -------- | --------------------------------------------------------------------------------------- |
| 定义的常量不带类型            | 定义的常量带类型 |                                                                                         |
| 预编译的时候处理             | 运行的时候处理  |                                                                                         |
| 只是简单的字符串替换,没有类型检查    | 有类型检查    | <p>#define N 2+3 //我们预想的N值是5，我们这样使用N<br>double a = N/2;  //我们预想的a的值是2.5，可实际上a的值是3.5</p> |
| 不能调试，因为在预编译的时候就已经替换了 | 可以调试     |                                                                                         |

> 参考：
>
> <https://blog.csdn.net/yi_ming_he/article/details/70405364>

## 2. 指针

不同于java，在C++中有指针的概念。它的类型必须是一个有效的C++类型，比如

```
int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */
```

所有指针的值的实际数据类型，不管是整型、浮点型、字符型，还是其他的数据类型，都是一样的，都是一个代表内存地址的长的十六进制数。**不同数据类型的指针之间唯一的不同是，指针所指向的变量或常量的数据类型不同。**

使用指针：

```cpp
#include <iostream>

using namespace std;

int main ()
{
    int  var = 20;   // 实际变量的声明
    int  *ip;        // 指针变量的声明

    ip = &var;       // 在指针变量中存储 var 的地址

    cout << "Value of var variable: ";
    cout << var << endl;

    // 输出在指针变量中存储的地址
    cout << "Address stored in ip variable: ";
    cout << ip << endl;

    // 访问指针中地址的值
    cout << "Value of *ip variable: ";
    cout << *ip << endl;

    return 0;
}
```

运行结果：

```
Value of var variable: 20
Address stored in ip variable: 0x7ffeee5be738
Value of *ip variable: 20
```

## 3. 引用

类似于java中的引用，是一个变量的别名，它是已存在变量的另一个名字，一旦把引用初始化为某个变量，就可以使用该引用名称或变量名称来指向变量。

**与指针的区别：**

* 不存在空引用。引用必须连接到一块合法的内存。
* 一旦引用被初始化为一个对象，就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
* 引用必须在创建时被初始化，指针可以在任何时间被初始化。

使用引用

```cpp
#include <iostream>

using namespace std;

int main ()
{
   // 声明简单的变量
   int    i;
   double d;

   // 声明引用变量
   int&    r = i;
   double& s = d;

   i = 5;
   cout << "Value of i : " << i << endl;
   cout << "Value of i reference : " << r  << endl;

   d = 11.7;
   cout << "Value of d : " << d << endl;
   cout << "Value of d reference : " << s  << endl;

   return 0;
}
```

运行结果

```
Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7
```

## 4. C++的四大特性

* 封装（把数据和操作数据的函数绑定在一起的机制）
* 抽象（仅向用户暴露接口而把具体实现隐藏起来的机制）
* 继承（可以多继承）
* 多态（用到虚函数和纯虚函数）

## 5. 指针引用和对象引用

在C++中获取一个对象的属性可以用->和.

比如：

```cpp
this->name;
person.name;
```

其中

->是指针类型，是指针引用，因为this是指针，所以只能使用->

.是实例化对象，类中普通成员的引用。

## 6. Include <> 和 ""区别

<>和""表示编译器在搜索头文件时的顺序不同，<>表示从系统目录下开始搜索，然后再搜索PATH环境变量所列出的目录，**不搜索当前目录**，""是表示从当前目录开始搜索，然后是系统目录和PATH环境变量所列出的目录。所以，系统头文件一般用<>，用户自己定义的可以使用""，加快搜索速度。

## 7. 函数定义的3种形参，对象，引用和指针

函数的形参有三种形式：

```cpp
void swap1(test t);//普通形参，不改变实参
void swap2(test& t);//引用作为形参，会改变实参
void swap3(test *t);//指针作为形参，会改变实参
```

* 第一种方式传递的是实参的值，传递是单向的，所以形参的改变不会影响实参；
* 第二种方式是传递变量的引用，因为引用指向的也是实参变量，所以这种方式会改变实参的值；
* 第三种方式是传递变量的指针，形参（指针变量）指向实参的变量地址，所以这种方式也会改变是实参的值。

为什么有了指针传递还要有引用传递呢，是因为指针这种方式不够直观，而且依旧和第一种一样采用的是值传递，只不过传递的值是变量的地址，下面看一下例子来对比一下这三种方式：

```cpp
#include <iostream>
#include "utils.h"

using namespace std;

class test {
public:
    int a;
    int b;
};

void swap1(test t);//普通形参，不改变实参
void swap2(test& t);//引用作为形参，会改变实参
void swap3(test *t);//指针作为形参，会改变实参

int main()
{
    test t1, t2, t3;
    t1.a = 1;
    t1.b = 2;
    t2 = t1; //在C++中赋值就是拷贝一下新的对象，所以不t2改变不影响t1
    t3 = t1;
    swap1(t1);
    swap2(t2);
    swap3(&t3);
    cout << "t1.a=" << t1.a << ",t1.b=" << t1.b << endl;
    cout << "t2.a=" << t2.a << ",t2.b=" << t2.b << endl;
    cout << "t3.a=" << t3.a << ",t3.b=" << t3.b << endl;

    return 0;
}

void swap1(test t){
    int tem = t.a;
    t.a = t.b;
    t.b = tem;
}

void swap2(test& t){
    int tem = t.a;
    t.a = t.b;
    t.b = tem;
}

void swap3(test *t){
    int tem = t->a;
    t->a = t->b;
    t->b = tem;
}
```

运行结果：

```
t1.a=1,t1.b=2
t2.a=2,t2.b=1
t3.a=2,t3.b=1
```

> 参考：
>
> <https://www.cnblogs.com/mylinux/p/4091583.html>
>
> <https://blog.csdn.net/chaipp0607/article/details/60151813>

## 8. 赋值与拷贝构造函数的区别

在c++中赋值语句`=`是拷贝了一份对象，所以新对象的改变不会影响原对象，不像java中赋值语句就是引用。在C++中B=A，就能把对象A的数据成员的值逐位复制给对象B。

拷贝构造函数是使用一个存在的对象构造另一个对象的函数，

区别：

1. 拷贝构造函数是一个对象初始化一块内存区域，这块内存就是新对象的内存区，而赋值函数是对于一个已经被初始化的对象进行赋值操作。
2. 在数据成员包含指针对象的时候，一种是复制指针对象，另一种是引用指针对象。拷贝构造函数大多数是复制，赋值函数是引用对象。
3. 实现不一样。拷贝构造函数通过参数对象，初始化产生一个对象。赋值函数则把一个新的对象赋值给一个原有的对象。所有如果原有的对象中有内存分配要先把内存释放掉，而且还要检查一下两个对象是不是同一个对象，如果是，不做任何操作，直接返回

**总结：**

**对象不存在，且没用别的对象来初始化，调用构造函数** **对象不存在，且用别的对象来初始化，调用拷贝构造** **对象存在，用别的对象给他赋值，就是赋值函数**

## 9. C++的编译步骤

1. 编写源代码
2. 编译器编译源代码为机器语言，称为目标代码
3. 将目标代码和其他代码链接起来，链接指的是将目标代码同使用的函数的目标代码以及一些标准启动代码组合起来，生成程序的运行阶段版本，包含该最终产品的文件被称为可执行代码。

![](https://3037548586-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnpynfpU0w3FBTJVlOK%2Fsync%2F13c2e643aebe10106c20204718d82820e2b0d393.png?generation=1589894634373144\&alt=media)

## 10. endl和\n的区别

都是换行，唯一的区别是endl保证程序继续运行前刷新输出（将其理解显示在屏幕上），而\n不能提供这样的保证。

## 11. extern "C"

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后，会指示编译器这部分代码按C语言的进行编译，而不是C++的。

**这个功能主要用在下面的情况：**

1、C++代码调用C语言代码

2、在C++的头文件中使用

3、在多个人协同开发时，可能有的人比较擅长C语言，而有的人擅长C++，这样的情况下也会有用到

## 12.  #include  \\

```
<climits> 头文件定义的符号常量
CHAR_MIN 　　　　　　char的最小值
SCHAR_MAX 　　　　　signed char 最大值
SCHAR_MIN 　　　　　 signed char 最小值
UCHAR_MAX 　　　　　unsigned char 最大值
SHRT_MAX 　　　　　　short 最大值
SHRT_MIN　　　　　　 short 最小值
USHRT_MAX　　　　　 unsigned short 最大值
INT_MAX　　　　　　　int 最大值
INT_MIN 　　　　　　　int 最小值
UINT_MAX 　　　　　　unsigned int 最大值
UINT_MIN 　　　　　　unsigned int 最小值
LONG_MAX　　　　　　long最大值
LONG_MIN 　　　　　　long最小值
ULONG_MAX 　　　　　unsigned long 最大值
FLT_MANT_DIG　　　　float 类型的尾数
FLT_DIG　　　　　　　 float 类型的最少有效数字位数
FLT_MIN_10_EXP　　　带有全部有效数的float类型的负指数的最小值（以10为底）
FLT_MAX_10_EXP 　　 float类型的正指数的最大值（以10为底）
FLT_MIN 　　　　　　　保留全部精度的float类型正数最小值
FLT_MAX　　　　　　　 float类型正数最大值
```

## 13. C++数组

声明数组的方式：

```cpp
char arr1[30];//数组名是一个指向数组中第一个元素的常量指针
array<char, 30> arr2; //使用模板类来声明数组
vector<char> arr2(30); //使用vector声明数组
```
