网站备案人授权书,大连零基础网站建设培训哪里有,如何兼职做网站,营口网站建设哪家好目录
C对C的加强
命名空间
为什么要使用命名空间
怎么使用命名空间
命名空间的定义
命名空间的使用
使用域解析符 ::
使用using声明
内联命名空间
嵌套命名空间
随时将新的成员加入命名空间
命名空间中 函数的声明和实现分开
无名命名空间
命名空间取别名
使用u…目录
C对C的加强
命名空间
为什么要使用命名空间
怎么使用命名空间
命名空间的定义
命名空间的使用
使用域解析符 ::
使用using声明
内联命名空间
嵌套命名空间
随时将新的成员加入命名空间
命名空间中 函数的声明和实现分开
无名命名空间
命名空间取别名
使用using申明命名空间中的某几个成员 可用
using 申明某个成员 容易造成名字冲突
using 申明制定成员函数 遇到函数重载
using申明整个命名空间 可以直接通过成员名 使用
C 头文件的现状
输入与输出
实用性加强
全局变量检测增强
c的函数形参必须有类型
如果函数没有参数建议写void
更严格的类型转换
if和while中可以使用逗号表达式
for循环基于范围的循环
register关键字
register关键字的作用
register修饰符在C语言中的几点限制
在C中依然支持register关键字
三目运算符的增强
使用using定义别名取代typedef
结构体类型增强重要
编辑 .hpp将类定义和实现放在一个文件里
c新增bool类型
cv属性
const关键字(重要)
volatile关键字
constexpr常量表达式
枚举变量
类型推导auto和decltype
语法格式的区别
对 cv 限定符的处理
对引用的处理
总结
引用
1.引用的定义
2.引用作为函数的参数
3.引用作为函数的返回值类型链式操作
4.常引用
5.引用的本质常量指针
动态内存分配
为什么初始化要置为空指针
如何避免野指针养成良好的编程习惯
new关键字与malloc函数的区别
1.new和delete操作基本类型的空间
2.new分配多维数组
3.new和delete操作类的空间
4.new申请对象数组
内联函数
宏函数和内联函数的区别重要背
内联函数的注意事项
函数重载重要
1.函数重载的条件背
2.函数重载的底层实现原理
函数的缺省参数重要
占位参数
类型转换
C语言的隐式转换和显示转换
C的类型转换运算符
static_cast type-id ( expression ) 常用
dynamic_cast type-id ( expression )
const_cast type-id ( expression )
reinterpret_cast type-id ( expression ) C对C的加强 如何解决多人协作开发的中产生的命名冲突问题 在c语言开发中通过static限定全局变量和函数仅在当前文件有效从而解决变量和函数命名冲突。 在C中使用命名空间。 命名空间
为什么要使用命名空间
一个中大型软件往往由多名程序员共同开发会使用大量的变量和函数不可避免地会出现变量或函数的命名冲突。当所有人的代码都测试通过没有问题时将它们结合到一起就有可能会出现命名冲突。
为了解决合作开发时的命名冲突问题C 引入了命名空间Namespace的概念。
命名空间将全局作用域分成不同的部分
不同命名空间中的标识符可以同名而不会发生冲突
命名空间可以相互嵌套
全局作用域也叫默认命名空间
怎么使用命名空间
命名空间的定义 /**
namespace 是C中的关键字用来定义一个命名空间语法格式为
namespace name
{变量函数类
}
name是命名空间的名字它里面可以包含变量、函数、类、typedef、#define 等最后由{ }包围
**/
// 定义一个命名空间名字叫NameSpaceA
namespace NameSpaceA
{int a 0;
}// 命名空间的定义可以嵌套
namespace NameSpaceB
{int a 1;namespace NameSpaceC{struct Teacher{char name[10];int age;};}
}
命名空间的使用
使用域解析符 ::
// 在这里用using声明了 NameSpaceA::a 它的意思是在声明以后的程序中如果出现
// 未指明命名空间的a就使用NameSpaceA命名空间里的a
// 如果要使用NameSpaceB命名空间中的a则仍需要使用这样的方式 NameSpaceB::a;
using NameSpaceA::a;
a 20; // 使用命名空间NameSpaceA中的a
NameSpaceB::a 30; // 使用命名空间NameSpaceB中的a
使用using声明
// using 声明不仅可以针对命名空间中的变量或者函数还可以对整个命名空间进行声明
// 这样的方式声明命名空间以后在后面使用未指定具体命名空间的变量或者函数产生命名冲突的时候
// 默认使用 NameSpaceB中的变量和函数
using namespace NameSpaceB;
a 10;printf (%d\n, NameSpaceB::a);
内联命名空间 #include iostream
using namespace std;
namespace A {namespace V1 {void print(){cout hello world! endl;}}namespace V2 {void print(){cout welcome to jscet!\n;}}inline namespace V3{void print() {cout 最新版本\n;}}
}int main()
{//新版本A::print();//旧版本A::V1::print();A::V2::print();return 0;
} 嵌套命名空间 随时将新的成员加入命名空间 命名空间中 函数的声明和实现分开 无名命名空间
无名命名空间 只能在 本源文件使用 命名空间取别名 使用using申明命名空间中的某几个成员 可用 using 申明某个成员 容易造成名字冲突 using 申明制定成员函数 遇到函数重载 using申明整个命名空间 可以直接通过成员名 使用 加作用域解决冲突 C 头文件的现状
1) 旧的 C 头文件如 iostream.h、fstream.h 等将会继续被支持尽管它们不在官方标准中。这些头文件的内容不在命名空间 std 中。
2) 新的 C 头文件如 iostream、fstream 等包含的基本功能和对应的旧版头文件相似但头文件的内容在命名空间 std 中。
注意在标准化的过程中库中有些部分的细节被修改了所以旧的头文件和新的头文件不一定完全对应。
3) 标准C头文件如 stdio.h、stdlib.h 等继续被支持。头文件的内容不在 std 中。
4) 具有C库功能的新C头文件具有如 cstdio、cstdlib 这样的名字。它们提供的内容和相应的旧的C头文件相同只是内容在 std 中。 可以发现对于不带.h的头文件所有的符号都位于命名空间 std 中使用时需要声明命名空间 std对于带.h的头文件没有使用任何命名空间所有符号都位于全局作用域。这也是 C 标准所规定的。
输入与输出 换行用于刷新缓冲区比如printf和cout需要换行来刷新才能输出
使用clog输出日志信息不需要换行就能输出
cerror用于输出错误信息也要加换行 实用性加强
C语言为弱语法语言某些类型不匹配但不会发生编译错误C为强语法语言必须严格类型匹配。
全局变量检测增强 c的函数形参必须有类型
c语言允许函数形参无类型可以传任意参数 c不允许
如果函数没有参数建议写void
c语言可以 c不可以 更严格的类型转换 if和while中可以使用逗号表达式 for循环基于范围的循环
for_each用法
#includealgorithm
#includeiostream
#includevectorvoid func(int n)
{std::cout n std::endl;
}int main()
{std::vectorint arr;arr.push_back(1);arr.push_back(2);std::for_each(arr.begin(), arr.end(), func);return 0;
}程序执行结果为
12
C11语法格式的 for 循环还支持遍历用{ }大括号初始化的列表
#include iostream
using namespace std;int main() {for (int num : {1, 2, 3, 4, 5}) {cout num ;}return 0;
}
程序执行结果为
1 2 3 4 5
在使用新语法格式的 for 循环遍历某个序列或数组时如果需要遍历的同时修改序列中元素的值实现方案是在参数处定义引用形式的变量
#include iostream
#include vector
using namespace std;int main() {char arc[] abcde;vectorcharmyvector(arc, arc 5);//for循环遍历并修改容器中各个字符的值for (auto ch : myvector) {ch;}//for循环遍历输出容器中各个字符for (auto ch : myvector) {cout ch;}return 0;
}
程序执行结果为
bcdef
register关键字
register关键字的作用
register修饰暗示编译程序相应的变量将被频繁使用的变量尽可能的将这个变量保存在CPU内部寄存器中而不是通过内存寻址来访问是为了提升它的运行速率。
register修饰符在C语言中的几点限制
1register变量必须是能被CPU所接受的类型。
这通常意味着register变量必须是一个单个的值并且长度应该小于或者等于整型的长度。不过有些机器的寄存器也能存放浮点数。
2因为register变量可能不存放在内存中所以不能用“”来获取register变量的地址。
3只有局部自动变量和形式参数可以作为寄存器变量其它如全局变量不行。
在调用一个函数时占用一些寄存器以存放寄存器变量的值函数调用结束后释放寄存器。此后在调用另外一个函数时又可以利用这些寄存器来存放该函数的寄存器变量。
4局部静态变量不能定义为寄存器变量。不能写成register static int a, b, c;
5由于寄存器的数量有限不同的cpu寄存器数目不一不能定义任意多个寄存器变量而且某些寄存器只能接受特定类型的数据如指针和浮点数因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器而任何多余的register修饰符都将被编译程序所忽略。
在C中依然支持register关键字
1、C编译器有自己的优化方式不使用register也可能做优化
2、C中可以取得register变量的地址
C编译器发现程序中需要取register变量的地址时register对变量的声明变得无效。
早期C语言编译器不会对代码进行优化因此register变量是一个很好的补充。
void func()
{register int iRegister;iRegister 90;cout iRegister endl;
}
三目运算符的增强
C语言三目运算符 ? : 如果条件为真返回表达式1否则返回表达式2 例如 int t (x y) ? x y: x - y;
C中三目运算符
#include stdio.h// C语言中表达式的结果 放在什么地方 寄存器
// 1
// 表达式返回的是一个值是一个数
// 在C中表达式返回的是变量本身// 2 如何做到的
// 让表达式返回一个内存空间..内存的首地址 指针
// 在C语言中如何实现C的效果// 3 本质
// C编译器自己做了取地址的操作
int main()
{int a 10;int b 20;// C中三目运算符返回的是变量本身所以可以作为左值使用(a b ? a : b) 90;// 在C语言中让三目运算符可以当左值使用可以通过返回变量地址实现*(a b ? a : b) 90;printf (%d, %d\n, a, b);return 0;
}
1C语言返回变量的值 C语言是返回变量本身
C语言中的三目运算符返回的是变量值不能作为左值使用
C中的三目运算符可直接返回变量本身因此可以出现在程序的任何地方
2注意三目运算符可能返回的值中如果有一个是常量值则不能作为左值使用
(a b ? 1 : b ) 30;
3C语言如何支持类似C的特性呢
当左值的条件要有内存空间C编译器帮助程序员取了一个地址而已
使用using定义别名取代typedef
传统使用typedef来重命名类型 C增加使用using来重命名类型 结构体类型增强重要 1.定义和使用
C语言中对象必须通过struct结构体名来定义
#include stdio.hstruct Stu{char name[20];int age;
};int main(int argc, char const *argv[])
{struct Stu stu1{小明,20};printf(stu1: %s age: %d\n, stu1.name, stu1.age);return 0;
}C中使用结构体名可以直接定义对象
#include iostream
using namespace std;struct Stu{char name[20];int age;
};int main(int argc, char const *argv[])
{Stu stu1{小明,20};cout stu1: stu1.name id :stu1.ageendl;return 0;
}2.C允许函数作为结构体的成员 3.引入权限修饰符
使用struct修饰成员默认为公共属性 使用class修饰成员默认为私有属性不可通过外部直接访问 可以通过公共成员间接访问内部私有成员
#include iostream
using namespace std;class Stu{
private:char name[20];int age;
public:void printf();
};void Stu::printf()
{cout stu1: name id :ageendl;
}int main(int argc, char const *argv[])
{Stu stu1;stu1.printf();return 0;
}4.C允许类的成员在定义时赋值
#include iostream
using namespace std;class Stu{
private:char name[20]{0};int age10;
public:void printf();
};void Stu::printf()
{cout stu1: name id :ageendl;
}int main(int argc, char const *argv[])
{Stu stu1;stu1.printf();return 0;
}5.this指针
#include iostream
#include stdlib.h
using namespace std;class Stu{
private:char name[20]{0};int age10;
public:void init();void printf();
};void Stu::init()
{this-age19;memcpy(this-name,小明,sizeof(小明));
}
void Stu::printf()
{cout stu1: name id :ageendl;
}int main(int argc, char const *argv[])
{Stu stu1;stu1.printf();stu1.init();stu1.printf();return 0;
}.hpp将类定义和实现放在一个文件里
#pragma onceclass Stack
{
public:void init(int max_len 1024);bool push(int num);bool pop();int get_top();bool full();bool empty();private:int m_top;int *m_stack;int m_max_len;
};void Stack::init(int max_len)
{m_stack new int[max_len];m_max_len max_len;m_top -1;
}bool Stack::full()
{return m_top m_max_len - 1;
}bool Stack::empty()
{return m_top -1;
}bool Stack::push(int num)
{if (!full()){m_top;m_stack[m_top] num;return true;}else{return false;}
}bool Stack::pop()
{if (!empty()){m_top--;return true;}else{return false;}
}int Stack::get_top()
{return m_stack[m_top];
}
c新增bool类型
bool类型拥有两个值 true false cv属性
「cv 限定符」是 const 和 volatile 关键字的统称
const 关键字用来表示数据是只读的也就是不能被修改
volatile 和 const 是相反的它用来表示数据是可变的、易变的目的是不让 CPU 将数据缓存到寄存器而是从原始的内存中读取。
const关键字(重要)
1、c和c中的const都是修饰变量为 只读。
2、c语言 严格准许 const修饰的是只读变量本质是 变量。 3、c的const 会对变量 优化
1如果以 常量 初始化const修饰的变量 编译器会将变量的值 放入符号常量表中不会立即给变 量开辟空间 2只有当对a 取地址时 编译器才会给a开辟空间只读变量 3如果以变量 初始化const修饰的只读变量没有符号常量表立即开辟空间 4如果以const修饰的是自定义类型的变量 也不会有符号常量表立即开辟空间 5c中尽量使用const代替define
const有类型可进行编译器类型安全检查。#define无类型,不可进行类型检查
在旧版本 C 中 如果想建立一个常量 必须使用预处理器” #define MAX 1024; 我 们定义的宏 MAX 从未被编译器看到过 因为在预处理阶段 所有的 MAX 已经被 替换为了 1024 于是 MAX 并没有将其加入到符号表中。 但我们使用这个常量获 得一个编译错误信息时 可能会带来一些困惑 因为这个信息可能会提到 1024 但是并没有提到 MAX.如果 MAX 被定义在一个不是你写的头文件中 你可能并不 知道 1024 代表什么 也许解决这个问题要花费很长时间。 解决办法就是用一个常 量替换上面的宏。
const有作用域而#define不重视作用域宏不能作为命名空间、结构体、类的成员而const可以。
volatile关键字
volatile“不稳定、易变的”禁止编译器进行任何形式的优化每次使用都必须“老老实实”取值操作 以常量初始化const修饰的只读变量可以通过volatile防止编译器进行优化可以通过访问地址进行变量值修改。 constexpr常量表达式 constexptr目的
修饰常量表达式提升程序执行效率即常量表达式是在编译阶段执行的可以修饰变量对象函数
表示方式
修饰普通常量修饰函数修饰模板修饰构造函数 区别函数模板和模板函数 关注后两个字前者是模板后者是函数 constexptr可以修饰函数模板函数模板实例化后得到模板函数 1区别constconstexptr常量表达式
const修饰的并不一定是常量表达式
constexptr修饰的一定是常量表达式
常量表达式要在程序编译时执行普通变量要在程序运行时执行
#include iostream
using namespace std;
int main()
{ const int a1 10; // a1是常量表达式。 const int a2 a1 20; // a2是常量表达式int a3 5; // a3不是常量表达式const int a4 a3; // a4不是常量表达式因为a3程序的执行到达其所在的声明处时才初始化所以变量a4的值程序运行时才知道。但编译没问题return 0;
}以上代码可正常编译。
说明了const声明的不一定就是常量表达式
因为a3不是常量表达式a4是常量表达式但是a3需要在a4前执行而且是运行时因此a4不能在编译时执行故说明const并不一定是常量表达式。
2定义普通常量与const等价
3修饰函数 a. 必须要有返回值 b. 返回值如果不是常量表达式函数修饰符constexptr会被自动忽略 c. 整个函数的函数体中不能出现非常量表达式之外的语句如for循环中声明int i 等using 指令、typedef 语句以及 static_assert 断言、return 语句除外。
4修饰模板如果模板函数实例化结果不是常量表达式修饰符constexptr会被自动忽略
5修饰构造函数只能用初始化列表
6constexptr的引用必须绑定到全局变量上
#include bits/stdc.h
using namespace std;
int num1 10;
int main(){constexpr int c num1;system(pause);return 0;
}举例
#include bits/stdc.h
using namespace std;
struct Test{int id;int age;//修饰构造函数,必须用初始化列表// constexpr Test(){// id 50;// age 100;// }constexpr Test(int idd, int a):id(idd), age(a){}
};
//常量表达式函数
constexpr int func1(){constexpr int a 10;int b 10;return b;//返回值既可以是常量表达式也可以是变量,如果是非常量constexptr会被自动忽略
}
// constexpr void func2(){//error:常量表达式函数需要返回值
// cout 无返回值constexptrendl;
// }
templatetypename T
constexpr T display(T t){return t;
}
int main(){constexpr Test t1{1, 15};//constexpr string str llx;//Error constexpr无法修饰 stringconstexpr int id t1.id;constexpr int age t1.age;cout func1() endl;Test t2{2, 20};Test res display(t2);cout constexptr修饰模板参数为结构体变量 res.id res.age endl;constexpr Test t3{3, 30};res display(t3);cout constexptr修饰模板参数为结构体变量 res.id res.age endl;system(pause);return 0;
}运行结果 具体可参考如下博客
cnullptr空指针常量、constexpr常量表达式
const与constexpr的区别
枚举变量
C基础知识 - 枚举类型_c 枚举类型_骆驼胡杨的博客-CSDN博客
类型推导auto和decltype
语法格式的区别
auto 和 decltype 都是 C11 新增的关键字都用于自动类型推导但是它们的语法格式是有区别的如下所示 auto varname value; //auto的语法格式 decltype(exp) varname [ value]; //decltype的语法格式 其中varname 表示变量名value 表示赋给变量的值exp 表示一个表达式方括号[ ]表示可有可无。 auto 和 decltype 都会自动推导出变量 varname 的类型 auto 根据右边的初始值 value 推导出变量的类型
decltype 根据 exp 表达式推导出变量的类型跟右边的 value 没有关系。
另外auto 要求变量必须初始化也就是在定义变量的同时必须给它赋值而 decltype 不要求初始化与否都不影响变量的类型。这很容易理解因为 auto 是根据变量的初始值来推导出变量类型的如果不初始化变量的类型也就无法推导了。
auto 将变量的类型和初始值绑定在一起而 decltype 将变量的类型和初始值分开虽然 auto 的书写更加简洁但 decltype 的使用更加灵活。
请看下面的例子
auto n1 10;
decltype(10) n2 99;auto url1 http://c.biancheng.net/cplus/;
decltype(url1) url2 http://c.biancheng.net/java/;auto f1 2.5;
decltype(n1*6.7) f2;
对 cv 限定符的处理
「cv 限定符」是 const 和 volatile 关键字的统称
const 关键字用来表示数据是只读的也就是不能被修改volatile 和 const 是相反的它用来表示数据是可变的、易变的目的是不让 CPU 将数据缓存到寄存器而是从原始的内存中读取。
在推导变量类型时auto 和 decltype 对 cv 限制符的处理是不一样的。decltype 会保留 cv 限定符而 auto 有可能会去掉 cv 限定符。
以下是 auto 关键字对 cv 限定符的推导规则
如果表达式的类型不是指针或者引用auto 会把 cv 限定符直接抛弃推导成 non-const 或者 non-volatile 类型。如果表达式的类型是指针或者引用auto 将保留 cv 限定符。
下面的例子演示了对 const 限定符的推导
//非指针非引用类型
const int n1 0;auto n2 10;
n2 99; //赋值不报错decltype(n1) n3 20;
n3 5; //赋值报错//指针类型
const int *p1 n1;auto p2 p1;
*p2 66; //赋值报错decltype(p1) p3 p1;
*p3 19; //赋值报错
在 C 中无法将一个变量的完整类型输出我们通过对变量赋值来判断它是否被 const 修饰如果被 const 修饰那么赋值失败如果不被 const 修饰那么赋值成功。虽然这种方案不太直观但也是能达到目的的。
n2 赋值成功说明不带 const也就是 const 被 auto 抛弃了这验证了 auto 的第一条推导规则。p2 赋值失败说明是带 const 的也就是 const 没有被 auto 抛弃这验证了 auto 的第二条推导规则。
n3 和 p3 都赋值失败说明 decltype 不会去掉表达式的 const 属性。
对引用的处理
当表达式的类型为引用时auto 和 decltype 的推导规则也不一样decltype 会保留引用类型而 auto 会抛弃引用类型直接推导出它的原始类型。请看下面的例子
#include iostream
using namespace std;int main() {int n 10;int r1 n;//auto推导auto r2 r1;r2 20;cout n , r1 , r2 endl;//decltype推导decltype(r1) r3 n;r3 99;cout n , r1 , r3 endl;return 0;
}
运行结果
10, 10, 20
99, 99, 99
从运行结果可以发现给 r2 赋值并没有改变 n 的值这说明 r2 没有指向 n而是自立门户单独拥有了一块内存这就证明 r 不再是引用类型它的引用类型被 auto 抛弃了。
给 r3 赋值n 的值也跟着改变了这说明 r3 仍然指向 n它的引用类型被 decltype 保留了。
总结
auto 虽然在书写格式上比 decltype 简单但是它的推导规则复杂有时候会改变表达式的原始类型而 decltype 比较纯粹它一般会坚持保留原始表达式的任何类型让推导的结果更加原汁原味。
从代码是否健壮的角度考虑我推荐使用 decltype它没有那么多是非但是 decltype 总是显得比较麻烦尤其是当表达式比较复杂时例如 vector nums; decltype(nums.begin()) it nums.begin(); 而如果使用 auto 就会清爽很多 vector nums; auto it nums.begin(); 在实际开发中人们仍然喜欢使用 auto 关键字因为它用起来简单直观更符合人们的审美。
引用 主要作用提高了程序的运行效率省去拷贝过程即空间分配和释放过程
比如在函数按引用传参不需要传参时变量之间赋值拷贝也不需要对参数开辟空间和释放。虽然指针也具有同样效果但是使用指针安全性差容易造成访问越界和野指针。
1.引用的定义
引用的本质就是给变量名取个别名 案例1:给普通变量取别名 案例2给数组取别名
void test02()
{int arr[5]{10,20,30,40,50};int n sizeof(arr)/sizeof(arr[0]);int (myArr)[5] arr;int i0;for(i0;in;i){coutmyArr[i] ;}coutendl;
}案例3给指针变量取别名 案例4给函数取别名 注意 2.引用作为函数的参数
函数内部可以 通过 引用 操作外部变量。 节约空间 3.引用作为函数的返回值类型链式操作 链式操作连续赋值操作 4.常引用
给常量取别名
void test10()
{ //int a 10;//errconst int a 10;//a就是10的别名//a 100;//errcoutaendl;
}不能通过常引用 修改 内容。
常引用 作为函数的参数防止函数内部修改外部的值。 5.引用的本质常量指针
int a10;
int b a;//b为a的别名 int * const b a;
b 100;//a的值为100 *b 100;
动态内存分配
指针在定义时要初始化为空指针C中为nullptr是对NULL的封装
为什么初始化要置为空指针
空指针代表0地址不可以对0地址进行操作
如何避免野指针养成良好的编程习惯
malloc、free是库函数。new、delete在C中被抽象为运算符可以被重载
new关键字与malloc函数的区别
new关键字是C的一部分malloc函数是C库提供的函数new以具体类型为单位进行内存分配malloc只能以字节为单位进行内存分配new在申请单个类型变量时可进行初始化mallo不具备内存初始化的特性
1.new和delete操作基本类型的空间
new 不用强制类型转换new在申请空间的时候可以 初始化空间内容 2.new分配多维数组 3.new和delete操作类的空间
malloc 不会调用构造函数 free 不会调用析构函数
new 会调用构造函数 delete 调用析构函数 4.new申请对象数组 内联函数
内联函数在编译阶段 将内联函数中的函数体 替换函数调用处。避免函数调用时的开销。
内联函数 必须在定义的时候 使用关键字inline修饰 不能在声明的时候使用inline
//函數声明的时候 不要使用inline
int my_add(int x, int y);
void test01()
{coutmy_add(100,200)endl;
}
//内联函数 在定义的时候使用inline
inline int my_add(int x, int y)
{return xy;
}内联函数作用内存空间换运行时间
宏函数用编译时间换内存空间 宏函数和内联函数的区别重要背
宏函数和内联函数 都会在适当的位置 进行展开 避免函数调用开销。
宏函数的参数没有类型不能保证参数的完整性。
内联函数的参数有类型 能保证参数的完整性。
宏函数在预处理阶段展开
内联函数在编译阶段展开
宏函数没有作用域的限制不能作为命名空间、结构体、类的成员
内联函数有作用域的限制能作为命名空间、结构体、类的成员
内联函数的注意事项
在内联函数定义的时候加inline修饰类中的成员函数 默认都是内联函数不加inline 也是内联函数有时候 就算加上inline也不一定是内联函数内联函数条件 不能存在任何形式的循环语句不能存在过多的条件判断语句函数体不能过于庞大不能对函数取地址
总结有时候不加inline修饰 都有可能是内联函数。内不内联 由编译器决定。
函数重载重要
函数重载 是c的多态的特性静态多态。
函数重载用同一个函数名 代表不同的函数功能。
1.函数重载的条件背
同一作用域函数的参数类型、个数、顺序不同 都可以重载。返回值类型不能作为重载的条
件
void printFun(int a)
{coutintendl;
}
void printFun(int a, char b)
{coutint charendl;
}
void printFun(char a, int b)
{coutchar intendl;
}
void printFun(char a)
{coutcharendl;
}
void test02()
{printFun(10);printFun(10, a);printFun(a, 10);printFun(a);
}c中 不能直接将函数名作为函数的入口地址为啥呢?
由重载的底层原理可知函数名和参数 共同决定函数的入口地址
2.函数重载的底层实现原理 函数的缺省参数重要
在函数声明处 给函数参数一个默认的值如果函数调用处用户没用传实参编译器就可以使用 这个默认的值。 注意点
如果函数的某个参数设置为默认参数 那么这个参数的右边的所有参数 都必须是默认参数。
int func(int a, int b, int c10);//正确
int func(int a, int b20, int c);//错误 c必须默认参数
int func(int a10, int b, int c);//错误 b c必须默认参数
int func(int a, int b10, int c20);//正确
int func(int a10, int b, int c20);//错误 b必须默认参数
int func(int a10, int b20, int c20);//正确
占位参数 占位参数 也可以是缺省参数默认参数 默认参数和函数重载同时出现 一定要注意二义性 类型转换 C语言的隐式转换和显示转换
参考以下博客
C语言【隐式类型转换】和【显式类型转换】的详解
关于显式类型转换以及隐式类型转换
震惊关于整型提升不得不说的那些事
C的类型转换运算符
static_cast type-id ( expression ) 常用
类似于C语言的强制转化保证代码的安全性和正确性
用途
相关类型转换例如整型、实型还可以将non-const对象转换为const对象但是它不能将一个const对象转型为non-const对象只有 const_cast能做到子类转父类 进行上行转换把派生类的指针或引用转换成基类表示是安全的进行下行转换把基类指针或引用转换成派生类表示时由于没有动态类型检查所以是不安全的。void *指针与其他类型指针之间的转换但不能对不同类型的指针进行转换
例
基本数据类型的转换如
char c1 0;
int i static_castint(c1);
char c2 static_castchar(i);
将空指针转换为目标类型的指针或反之如
int i 0;
int *pa i;
void *pv static_castvoid*(pa);
int *pi static_castint*(pv);
但不能对不同类型的指针进行转换
int i 0;
int *pa i;
char *pc static_castchar*(pa); //编译报错invalid static_cast from type int* to type char*
long *pl static_castlong*(pa); //编译报错invalid static_cast from type int* to type long int*
对于自定义类型如果类定义转型运算符那么也可以通过static_cast对类对象进行转型
#include iostream
using namespace std;class A{
public:operator int(){return 1;}operator char(){return a;}
};int main()
{A a;int ai static_castint(a);char ac static_castchar(a);coutaiaiendl; //输出ai1coutacacendl; //输出aca
} 对于自定义类型的指针通过static_cast将派生类指针转换为基类指针是安全的反之将基类指针转换为派生类指针是不安全的建议类指针之间的转换都通过dynamic_cast进行。
dynamic_cast type-id ( expression ) dynamic_cast 主要用于执行“安全的向下转型(safe downcasting)”也就是说要确定一个对象是否是一个继承体系中的一个特定类型。支持父类指针到子类指针的转换这种转换时最安全的转换。它是唯一不能用旧风格语法执行的强制类型转换也是唯一可能有重大运行时代价的强制转换。
const_cast type-id ( expression ) const_cast一般用于强制消除对象的常量性。它是唯一能做到这一点的C风格的强制转型。这个转换能剥离一个对象的const属性也就是说允许你对常量进行修改。
怎么使用自己制作的静态库或动态库做项目。
reinterpret_cast type-id ( expression ) reinterpret_cast 是特意用于底层的强制转型导致实现依赖就是说不可移植的结果例如将一个指针转型为一个整数。这样的强制类型在底层代码以外应该极为罕见。操作 结果只是简单的从一个指针到别的指针的值得二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。
处理无关类型的转换。不安全既不在编译器期也不在运行期进行检查安全性完全由程序员决定。