基础汇总
c++总的思想角度是,告诉编译器要干嘛。以及内存上的管理。
相关关键词都在指示,将内存如何安排。
extern
means it is only a declaration, its definition is later or external to this file, asking the compiler not to assign memory or generate code
内存对齐
构造与析构函数
1
2
3
4
5
6
7
8
9
10
11
12
13
141、是否在析构函数抛出异常:
1异常点之后的程序如果有释放资源,会造成诸如资源泄漏
2通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。
3把异常完全封装在析构函数内部,决不让异常抛出函数之外
2、构造函数是否可以抛出异常:
1构造函数可以抛出异常。构造函数中尽量不要抛出异常。
2既需要分配内存,又需要抛出异常时要特别注意防止内存泄露的情况发生。因为在构造函数中抛出异常,在概念上将被视为该对象没有被成功构造,因此当前对象的析构函数就不会被调用,就会造成内存泄漏。
3同时,由于构造函数本身也是一个函数,在函数体内抛出异常将导致当前函数运行结束,并释放已经构造的成员对象,包括其基类的成员,即执行直接基类和成员对象的析构函数
3、构造函数和析构函数可以调用虚函数吗:
1当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数,那么此时派生类的构造函数并未执行,所调用的函数可能操作还没有被初始化的成员,将导致灾难的发生。
2即使想在构造函数中实现动态联编,在实现上也会遇到困难。这涉及到对象虚指针(vptr)的建立问题。一个类的构造函数在执行时,并不能保证该函数所能访问到的虚指针就是当前被构造对象最后所拥有的虚指针,因为后面派生类的构造函数会对当前被构造对象的虚指针进行重写,因此无法完成动态联编堆栈
1
2
31、堆栈和缓存的区别
1栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
2堆是存放在二级缓存中,堆的首地址放在一级缓存缓存中,分配和释放会产生系统调用,由用户态进入内核态,所以速度会慢一些安全漏洞、内存越界
1
野指针
1
2
3
4
5
6
7
8
9
10“野指针”不是NULL指针,是指指向“垃圾”内存的指针。即指针指向的内容是不确定的。
产生的原因:
1)指针变量没有初始化。因此,创建指针变量时,该变量要被置为NULL或者指向合法的内存单元。
2)指针p被free之后,没有置为NULL,让人误以为p是个合法的指针。
3)指针跨越合法范围操作。不要返回指向栈内存(非静态局部变量)的指针或引用。
可能后果:
若操作系统将这部分已经释放的内存重新分配给另外一个进程,而原来的程序重新引用现在的迷途指针,向其中写入数据,则这部分程序内容将被破坏,而导致程序错误。这种类型的程序错误,通常会导致segment fault和一般的保护错误。
其他常见错误:
返回一个基于栈分配的局部变量的地址时,一旦调用的函数返回,分配给这些变量的空间将回收,此时它们拥有的是垃圾值,如return &num,如果要使它的生命周期加长,应该将其声明为staticSTL容器及常见算法
http://vernlium.github.io/2019/12/29/C-STL%E5%B8%B8%E7%94%A8%E5%AE%B9%E5%99%A8API%E6%80%BB%E7%BB%93/
https://blog.csdn.net/weixin_43150428/article/details/82469933
tuple
array container
range-base for
initializer lists
delegate/inheriting constructors
nullptr
inline
1
2
3
4
51、内联展开避免函数中断调用开销
2、宏:内联函数在运行时可调试,而宏定义不可以;编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;内联函数可以访问类的成员变量,宏定义则不能;在类中声明同时定义的成员函数,自动转化为内联函数
inline一般只用于如下情况:
一个函数不断被重复调用函数只有简单的几行,且函数不包含for、while、switch语句,递归。unordered_table
shared_ptr/weak_ptr
regexp
cost of exception handling
type trait
strong exception gaurantee
CRTP
smart pointer
std::function and function pointer
runtime cost of lambda function
虚继承
Rvalue reference
- function/bind 适配器、取反器等
call operator被重写了,即()被重写。避免函数调用开销,作为inline。可以加入全局变量,以面向对象的角度考虑。支持泛型。
bind即将某个参数固定为,第一个或第二个操作数。
函数适配器,就像复合函数,将各个类型的函数进行组合。进行一些自定义来能够将自己的函数可以用在适配器里。
Lambda expression and closure
auto/decltype
static_assert
可变模板参数
指针和引用
1
2
3
4
5
61、指针可以是null,是对象
2、引用必须初始化,不是对象,且不能重新赋值
Using Reference when you find
1、It always represents a non-null object
2、And it will not represent any other objects
全局作用域
1
::
static
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1、定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。
2、在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。
即,需要class享有且隐藏在类内,或者,只受某个函数控制而不是受类控制。
3、
全局(静态)存储区:分为 DATA 段和 BSS 段。这段数据在程序刚开始运行时就完成初始化。
DATA 段(全局初始化区)存放初始化的全局变量和静态变量;
BSS 段(全局未初始化区)存放未初始化的全局变量和静态变量。程序执行之前已经为0。
4、面向过程:
静态全局变量 -- 只能在本文件中访问,不能在其它文件中访问,即便是 extern 外部声明也不可以
静态局部变量 -- 程序运行结束以后才释放
静态函数 -- 只能在本文件中调用,不能被其他文件调用
面向对象:
静态成员变量
静态成员函数
static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为 0。
(4)不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用 static 修饰。
(5)考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用 static)。
malloc free 和new delete
1
2
3
4
5
61、malloc只分配指定大小的堆内存空间,而new可以根据对象类型分配合适的堆内存空间,当然还可以通过重载operator new 自定义内存分配策略,其次还能够构造对象
2、free释放对应的堆内存空间,delete,先执行对象的析构函数,在释放对象所占空间。
3、malloc与free是C++/C 语言的标准库函数,new/delete 是C++的运算符。
4、malloc返回类型是void*,使用时需要类型转换,而new在分配时,编译器能够根据对象类型自动计算出大小,返回类型是指向对象类型的指针,其封装了sizeof和类型转换功能,实际上new分为两步,第一步是通过调用operator new函数分配一块合适,原始的,未命名的内存空间,返回类型也是void *,而且operator new可以重载,可以自定义内存分配策略,甚至不做内存分配,甚至分配到非内存设备上,而malloc无能为力,第二步,调用构造函数构造对象,new将调用constructor,而malloc不能;delete将调用destructor,而free不能
总结:1、大小or分配策略是否可自定义 2、析构和构造函数 3、库函数和运算符(重载)const
1
2
3
4
5
6
7
8
9
10
11
12
13
141、变量 初始化后,不被改变
2、函数
1. 修饰返回值 const int func() -- 不能改变返回值
2. 修饰参数 int func(const ) -- 函数体内不能改变参数
3. 修饰成员函数 int func() const -- 函数体内不能改变成员变量的值
3、指针
1. const int* 、int const*-- 常量指针,pointer指向的变量的值不能变,pointer可指向其他对象
2. int* const -- 指针常量,可以改变指针指向变量的值,pointer不能指向其他对象
4、对象 const对象只能调用const成员函数,不能调用普通函数
const int* const p=&i;多态
1
2
31、虚函数的类 至少有一个(多继承会有多个)一维的虚函数表叫做虚表(virtual table),属于类成员,虚表的元素值是虚函数的入口地址,在编译时就已经为其在数据端分配了空间
2、确定的虚函数对应virtual table中一个固定位置n,n是一个在编译时期就确定的常量,所以,使用vptr加上对应的n,就可以得到对应的函数入口地址。C++采用的这种绝对地址+偏移量的方法调用虚函数,查找速度快执行效率高,时间复杂度为O(1)c++ 与java .NET
1
2
3
4
5
6
7
8
9
10第一,C++的难不仅仅在于其静态结构体系,还有很多源于语言设计上的包袱,比如对C的兼容,比如没有垃圾收集机制,比如对效率的强调,等等。一旦把这些包袱丢掉,设计的难度确实可以大大下降。
第二,Java和.NET的核心类库是在C++十几年成功和失败的经验教训基础之上,结合COM体系优点设计实现的,自然要好上一大块。事实上,在Java和.NET核心类库的设计中很多地方,体现的是基于接口的设计,和真正的基于对象的设计。有了这两个主角站台,“面向类的设计”不能喧宾夺主,也能发挥一些好的作用。
第三,Java和.NET中分别对C++最大的问题——缺少对象级别的delegate机制做出了自己的回应,这就大大弥补了原来的问题。
尽管如此,Java还是沾染上了“面向类设计”的癌症,基础类库里就有很多架床叠屋的设计,而J2EE/Java EE当中,这种形而上学的设计也很普遍,所以也引发了好几次轻量化的运动。
至于.NET,我听陈榕介绍过,在设计.NET的时候,微软内部对于是否允许继承爆发了非常激烈的争论。很多资深高人都强烈反对继承。至于最后引入继承,很大程度上是营销需要压倒了技术理性。尽管如此,由于有COM的基础,又实现了非常彻底的delegate,所以 .NET 的设计水平还是很高的。它的主要问题不在这,在于太急于求胜,更新速度太快,基础不牢。当然,根本问题还是微软没有能够在Web和Mobile领域里占到多大的优势,也就使得.NET没有用武之地。命名规范
指针:p;指针指针:pp;句柄:h;引用:ref;array rg
成员变量:m_ ;全局变量 :g_ ;静态变量:s_ ;类class:C ;接口:I
i fl ui ch ul(ULONG) dw fn w(USHORT, SHORT, WORD) b v d f(BOOL) hr(HRESULT)
c++ 与 c
1
2
3
41、C面向过程,C++面向对象--Support data abstraction
2、Protect legacy codes
3、More safe
4、c++支持面向对象、基于对象、基于过程、泛型编程
reference:
https://blog.csdn.net/myan/article/details/5928531
本文链接: https://satyrswang.github.io/2021/04/11/c-tips/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!