提醒编译器它后面所定义的变量随时都可能改变,因此编译后的程序每次需要存储或读取该变量时,都从变量地址中读取数据。
Volatile的作用:
(1) 阻止编译器为了提高速度将一个变量缓存到寄存器内不写回
(2) 阻止编译器调整操作volatile变量的指令顺序
但是volatile能够阻止编译器调整顺序,也无法阻止CPU动态调度换序,例如:
解决方法:barrier(): 优化内存屏障(联系到内核同步机制)
https://blog.csdn.net/benpaobagzb/article/details/51050633
Barrier I/O:硬件级别的同步
应用场景:
(1) 中断服务程序中修改的供其他程序检测的变量
(2) 多任务环境下个任务见共享的标志
(3) 存储器映射的硬件存储器
在C语言中,static的用法:
(1) 在函数体,一个被声明为静态的变量在函数调用过程中值不变
(2) 在模块内(函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但不能被模块外其他函数访问,他是一个本地全局变量
(3) 在模块内,一个被声明为静态的函数只能被该模块内其他函数调用
在C++中,static的用法:
类的静态成员或方法不属于类实例,而属于类本身,并在所有实例间共享,调用时应用类名+::来引用
与define相比,const内存效率更高,编译器通常将const 变量保存在符号表中,而不会分配存储空间,这使得它成为一个编译期间的常量,没有存储和读取的操作
防止执行隐式类型转换
static_cast, const_cast, dynamic_cast, reinterpret_cast
语法为:***_cast
(1) static_cast
基本与拥有与C旧式转型相同的威力与意义,以及相同的限制。如:
int firstNum,secondNum;
double res =(double)firstNum / secondNum; //旧式C语法
double res =static_cast(firstNum) / secondNum; //新式C++转型符
(2) const_cast
用来改变表达式中的常量性(constness)或易变性(volatileness)。如:
int num;
const int *cpNum =#
int *pNum =cpNum; //error:cannot convert from 'const int *' to 'int *'
int *pNum = (int *)cpNum; //旧式C
int *pNum =const_cast(cpNum); //新式C++const_cast移除常量性
(3) dynamic_cast
用来执行继承体系中“安全的向下转型或跨系转型动作”。如:可以利用dynamic_cast将“指向base class object 的pointer或reference”转型为“指向derived class object的pointer或reference”。如果转型失败,会以一个null指针或一个exception 表现出来(指针转型失败返回null,引用转型失败抛出异常)
class CBase { };
class CDerived:public CBase { };
CDerived dc;
CDerived *dp =&dc;
CBase *bp =dynamic_cast(dp); //使用dynamic_cast将指向继承类的指针转化为指向基类的指针
CBase &br =dynamic_cast(dc); //使用dynamic_cast将指向继承类的引用转化为指向基类的引用
(4) reinterpret_cast
最常用的用途是转换"函数指针"类型。
typedef void(*funcPtr)(); //funcPtr是个无参数返回值为void型的函数指针类型
int iFunc(){ return0; } //iFunc为一个无参数返回值为int型的函数
void func( funcPtrf ){} //func函数的参数是一个类型为funcPtr类型的函数指针
int main(){
func(iFunc()); //error:cannotconvert parameter 1 from 'int' to 'void (__cdecl *)(void)'
func(reinterpre_cast(iFunc); //right!reinterpre_cast将返回值为int 的函数转化为返回值为void的函数
}
补充及修正:
1.static_cast与dynamic_cast
Static_cast 常用于数据类型之间的转换,它也可以实现dynamic_cast中的转换效果,当实现上行转换(继承类转换基类)时,二者可以实现相同的转换效果,当实现下行转换(基类转继承类)时,由于static_cast没有动态类型检查机制,转换可能不安全。而正是由于static_cast不作类型检查,它的转换比dynamic_cast要快。
2. reinterpret_cast
reinterpret_cast字面意思是重新解释,主要是将数据从一种类型的转换为另一种类型。所谓“通常为操作数的位模式提供较低层的重新解释”也就是说将数据以二进制存在形式的重新解释。
int i;
char *p = "Thisis a example.";
i =reinterpret_cast(p);
此时结果,i与p的值是完全相同的。reinterpret_cast的作用是说将指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,一个明显的现象是在转换前后没有数位损失。因此,reinterpret_cast允许转换一个指针为其他类型的指针,也允许将一个指针转换为整数类型,反之亦然。这个操作符能够在非相关的类型之间进行转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝,在类型之间指向的内容不做任何类型的检查和转换。这是一个强制转换。使用时有很大的风险,慎用之。比如:
class A { public:int m_a; };
class B { public:int m_b; };
class C : public A,public B {};
C c;
printf("%p, %p,%p\r\n", &c, reinterpret_cast(&c), static_cast(&c));
前两个的输出值是相同的,最后一个则会在原基础上偏移4个字节,这是因为static_cast计算了父子类指针转换的偏移量,并将之转换到正确的地址(c里面有m_a,m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换。因此, 你需要谨慎使用 reinterpret_cast.
(1)封装
封装是在设计类的一个基本原理,是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体。封装是一个抽象的模型,该模型对外提供服务,而任何使用该模型的用户不需要知道模型是如何运作的。
(2)继承
访问权限
public: 所有都能访问
protected: 成员可以被派生类对象访问但不能被该类型的普通用户访问
private:只能由基类的成员和友元访问
基类的protected可以被其派生类访问,派生类protected不能被其基类访问
友元关系不能继承,基类的友元对派生类的成没有特殊访问权限,如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。
(3)多态
多态表示不同对象可以执行相同的操作,但要通过他们自己的实现代码来执行。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
(1)静态多态:由重载和模板实现。
(2)动态多态:由虚函数实现的。
重载(overload)指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。
重写(也称为覆盖 override)是指派生类重新定义基类的虚函数,virtual。
重定义(也成隐藏):不在同一个作用域(分别位于派生类与基类)
虚函数的实现原理:虚函数表(vtbl),表中每一项指向一个虚函数地址,虚函数表具有继承性和多态性。虚函数表是在编译时就建立了。
构造函数不能,析构函数尽可能定义成虚函数,虚函数表是在构造函数中改写的
析构函数:虚函数
都不可以。
内联函数需要在编译阶段展开,而虚函数是运行时动态绑定的,编译时无法展开;
构造函数在进行调用时还不存在父类和子类的概念,父类只会调用父类的构造函数,子类调用子类的,因此不存在动态绑定的概念;
静态成员函数是以类为单位的函数,与具体对象无关,虚函数是与对象动态绑定的,因此是两个不冲突的概念。
1)内部类主要是为了避免命名冲突;(内部类定义为public)
2)为了隐藏名称(内部类定义为private/protected)
类的大小:
1.为类的非静态成员数据的类型大小之和.
2.有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针).
3.为了优化存取效率,进行的边缘调整.
4 与类中的构造函数,析构函数以及其他的成员函数无关
空类的大小:(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以空类的大小为1.
不能实例化的类被叫做抽象类,它们只提供部分实现,但是另一个类可以继承它并且能创建它们的实例。
"一个包含一个或多个纯虚函数的类叫抽象类,抽象类不能被实例化,进一步一个抽象类只能通过接口和作为其它类的基类使用."
一个抽象类可以包含抽象和非抽象方法,当一个类继承于抽象类,那么这个派生类必须实现所有的的基类抽象方法。
接口描述了类的行为和功能,而不需要完成类的特定实现。接口和抽象类都是继承树的上层,他们的共同点如下:
1) 都是上层的抽象层。
2) 都不能被实例化
3) 都能包含抽象的方法,这些抽象的方法用于描述类具备的功能,但是不比提供具体的实现。
他们的区别如下:
1) 在抽象类中可以写非抽象的方法,从而避免在子类中重复书写他们,这样可以提高代码的复用性,这是抽象类的优势;接口中只能有抽象的方法。
2) 一个类只能继承一个直接父类,这个父类可以是具体的类也可是抽象类;但是一个类可以实现多个接口。
#include <> :引用标准库头文件,编译器从标准库目录开始搜索
#incluce " " :引用非标准库的头文件,编译器从用户的工作目录开始搜索
(1)初始化要求不同:引用在创建时必须初始化,指针不必
(2)可修改性不同:引用一旦被初始化为指向一个对象,他就不能改变为另一个对象的引用,指针则不一样。
(3)不存在空引用,但是存在空指针
(4)测试需要的区别:由于引用不会指向空值,使用引用之前不需要测试其合法性,而指针则需要。因此引用代码效率比指针高。
C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。
C++中struct能包含成员函数吗? 能!
C++中struct能继承吗?能!!
C++中struct能实现多态吗?能!!!
C语言中struct智能定义成员变量,不能定义成员函数,但是可以有函数指针。
struct和class有什么区别?
1)默认的继承访问权限。struct是public的,class是private的。
2)默认的数据访问控制。struct是public的,class是private的。
3)“class”还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。
栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构”。可以简单理解为:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。
(未完待续)
智能指针是一种资源管理类,通过对原始指针进行封装,在资源管理对象进
行析构时对指针指向的内存进行释放;通常使用引用计数方式进行管理,一
个基本实现如下:
https://blog.csdn.net/xiaoding133/article/details/11662183
模板特例化用于当一个数据类型需要进行不同的处理和实现的情况。
#include
using namespace std;
template
T multIt(T x) {
for(int ii=0; ii(xx) << endl;;
}
类模板:
类模板定义:template
类模板特例化:template <> class MyTemplateClass
函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。即函数模板允许隐式调用和显式调用而类模板只能显示调用。这期间有涉及到函数模板与模板函数,类模板与模板类的概念 (类似于类与类对象的区别)
使用模板的缺点:不当的使用模板会导致代码膨胀,集二进制代码臃肿松散,影响程序运行效率
解决方法:把C++模板中与参数无关的代码分离出来
(1)函数模板特例化
(2)类模板特例化
(3)部分模板特例化 全部模板特例化
(4)使用函数模板对普通函数进行泛型化