头文件中ifndef/define/endif
的作用
保证即使头文件被包含多次,也只定义一次,起到预编译保护作用。
extern “C”
程序中的变量或函数,经C
和C++
编译器编译后后,符号不一样,会导致编译后的C
代码在符号库中找不到。extern “C”
告诉编译器这是C
代码,按照C
的方式编译。
所以,extern “C”
的目的是解决符号匹配问题,实现C
和C++
混合编程。
const
和#define
- 都可以定义常量,
const
用途更广,比如修饰函数返回值和参数 const
有数据类型,编译器可以对其进行类型安全检查,对#define
只会进行字符替换
sizeof
与strlen
sizeof
是运算符,strlen
是函数sizeof
可以用类型做参数,strlen
只能用\0
结尾的char *
指针和引用
- 定义指针时可以不初始化;定义引用时必须初始化
- 指针赋值是把指针指向另一个对象;引用赋值是修改引用绑定的对象的值
在底层,引用变量由指针按照指针常量的方式实现,所以int i = 2;int* const gi = &i;
和int i = 2;int &gi = i;
是一致的。
空指针、野指针和悬垂指针
- 空指针:指向地址为空的指针(NULL指针)。可以被多次
delete
- 野指针:指向垃圾内存的指针。产生原因是创建时未初始化,它将会随意指向一个内存地址
- 悬垂指针:动态创建的对象被释放或回收了,但是指向该对象的指针未做任何修改,仍旧指向已经回收的内存地址
malloc/free
和new/delete
malloc/free
是C/C++
标准库函数,new/delete
是C++
运算符- 申请的是堆里面的内存空间
malloc
不会做初始化,new
有默认的初始化同时可以指定初始化。对于类类型而言,对象在创建的时候要自动执行构造函数,消亡之前要调用析构函数,malloc/free
不能满足要求。malloc/free
是库函数而不是运算符,不在编译器控制之内,不能把执行构造函数和析构函数的任务强加给它,因此,C++
还需要malloc/free
#pragma once
和#ifndef/#define/#endif
#pragma once
是编译相关,某些编译器可能不能用,移植性差一些;#ifndef/#define/#endif
是语言相关#ifndef/#define/#endif
依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含;缺点是如果不同头文件的宏名不小心“撞车”,可能会导致头文件明明存在,编译器却说找不到声明的状况#pragma once
由编译器提供保证同一个文件不会被包含多次。“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件,所以对应的缺点就是如果某个头文件有多份拷贝,将不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正
NULL
和nullptr
在C
中,NULL
通常定义为#define NULL ((void *)0)
,void *
可以隐式转换成其他类型
而C++
是强类型的,void *
不能隐式转换成其他指针类型,所以通常情况下编译器在头文件中会这样定义NULL
:
1 | #ifdef __cplusplus |
所以C++
中用0
表示空指针。不过还是有缺陷不完美,处理重载函数的时候会出现问题。于是C++11
引入了nullptr
表示空指针
智能指针
最主要是为了解决内存释放问题。对于一个大型项目,我们在这里申请了一块内存,而在离这里很远的各个地方需要我们释放这块内存的时候,我们常常忘记这个操作,而且有时发现问题还很难排查出来。智能指针的目的就是让程序自动销毁这块内存,而不是靠程序员手动销毁。(未完待续)
打赏