目录
- POD类型
- 左值和右值
- 静态全局变量(static)
- 类型转换
- const/constexpr
- const
- constexpr
- C++中的关键字
- union
- 基础知识点
- 编译与函数参数入栈总结
- 一些常见用法归纳:
POD类型
平凡的和标准布局的——貌似和深度探索C++对象模型中关于按位拷贝冲突
- 平凡的定义:符合以下四点定义:是不是可以总结为:构造,析构,拷贝/移动构造/赋值运算符 都是自动生成,不包含虚函数/虚基类
- 有平凡的构造和析构:即构造和析构什么都不干,而一旦定义了构造或析构,即使不含参数,实现为空,也不再平凡,需定义成default;一般默认生成即可
- 平凡的拷贝构造和移动构造:平凡的拷贝构造等同于使用memcpy进行类型的构造;默认生成或显式定义成default
- 平凡的拷贝赋值运算符和移动赋值运算符,这基本上同平凡的拷贝构造和平凡的移动构造类似
- 不能包含虚函数和虚基类
- 标准布局:
- 所有非静态成员都有相同的访问权限
- 类或结构体继承时,满足以下两种情况之一:就是非静态成员不能同时出现在基类和派生类中
- 派生类中有非静态成员,且只有一个仅包含静态成员的基类
- 基类有非静态成员,而派生类没有非静态成员
- 类中第一个非静态成员的类型与其基类不同
- 没有虚函数和虚基类
- 所有非静态数据成员均符合标准布局类型,其基类也符合标准布局
- pod类型相关的类模板工具
#include<type_traits>
:template<typename T>struct std::is_trivial
的成员value可以用于判断T类型是否是一个POD类型;除了类和结构体之外,is_trivial可以对内置的标量类型数据及数组类型进行判断template<typename T>struct std::is_standard_layout
的value可以判断类型是否是一个标准布局的类型template<typename T>std::is_pod<T>::value
可以判断一个类型是否是POD,T可以是基本类型
- 使用POD类型有什么好处:
- 提供对C内存布局兼容,C++程序和C函数进行相互操作,因为POD类型的数据在C与C++间的操作总是安全的
- 字节赋值:可以安全的使用memset和memcpy对POD类型进行初始化和拷贝等操作
- 保证了静态初始化的安全有效。静态初始化在很多时候能够提高程序的性能,而POD类型的对象初始化往往更简单—(比如放入目标文件的bss段,在初始化中直接赋值为0)。
左值和右值
- 左值:可以取地址,有名字的就是左值,反之就是右值(更简单的,=左边的就是左值,右边的就是右值),调用一个返回引用的函数得到的是左值,其他返回类型得到右值,特别的,可以为返回类型是非常量引用的函数的结果赋值
- 右值:C++11中的右值分为纯右值和将亡值
- 纯右值:之前的右值概念
- 将亡值:C++11中新增的跟右值引用相关的表达式,如返回右值引用T&&的函数返回值,或者转换为T&&的类型转换函数的返回值,std::move的返回值
- 右值引用:对一个右值进行引用的类型;右值引用也必须立即初始化;右值引用是为了支持移动操作而引入的
- 一般右值不具有名字,通常情况下只能从右值表达式获取其引用,如
T&& t = ReturnRValue();
函数返回的右值在表达式语句结束后生命就结束了,通过右值引用的声明,该右值又重获新生,其声明周期和右值引用类型变量t的生命周期一样,不过需要注意,能够声明右值引用t的前提是ReturnRvalue返回的是一个右值 - 左值引用和右值引用都是都必须立即初始化,左值引用是具名变量值的引用,右值引用是不具名变量值的引用
- 右值引用不能绑定到任何左值,只能绑定到一个将要销毁的对象,如
int c; int &&d=c;
是不能通过编译的 - 左值引用是否可以绑定到右值?
- 右值引用的来由从来就跟移动语义紧密相关,这是右值存在的最大价值之一
- 为什么不使用常量右值引用?
- 一. 右值引用主要是为了移动语义,而移动语义需要右值是可以被修改的;
- 二,如果要引用右值且不可以更改,常量左值就足够了
- 一般右值不具有名字,通常情况下只能从右值表达式获取其引用,如
T& e = ReturnRvalue(); // 编译错误
const T& f = ReturnRvalue(); // 编译成功,常量左值引用在C++98中是个万能的引用类型,可以接受非常量左值,常量左值,右值对其进行初始化,而且在使用右值对其进行初始化的时
// 候常量左值引用还可以像右值引用一样将右值的生命期延长。
所以说,在定义一个移动构造函数的时候,定义一个常量左值引用的拷贝构造函数,是一种非常安全的设计——移动不成还可以执行拷贝
const bool & judge = true; //
const bool judge2 = true;
// 上面两条的区别就是:前者直接使用右值并为其续命,后者的右值在表达式结束后就销毁了
- 判断是否是引用类型/左值引用/右值引用:#include<type_traits>,通过类模板的成员value获取
- is_rvalue_reference
- is_lvalue_reference
- is_reference
- std::move:并不能移动任何东西,只是将左值强制转成右值,从实现上看相当于static_cast<T&&>(lvalue)。注意:被转换的左值,其生命周期并没有随着左右值的转化而改变,就是说,lvalue的析构不能依赖于move的实现,所以要析构掉就要靠自己?
静态全局变量(static)
- static修饰的全局变量的声明与定义同时进行,即当你在头文件中使用static声明了全局变量,同时它也被定义了,如果没有明确的值会赋值为0(NULL, “”),不像其他变量不明确
- static变量只会被初始化一次,下次进来的时候不会重新执行,以此特性可以使用单例模式
- static修饰的全局变量的作用域只能是本身的编译单元。在其他编译单元使用它时,只是简单的把其值复制给了其他编译单元,其他编译单元会另外开个内存保存它,在其他编译单元对它的修改并不影响本身在定义时的值。即在其他编译单元A使用它时,它所在的物理地址,和其他编译单元B使用它时,它所在的物理地址不一样,A和B对它所做的修改都不能传递给对方。这也是不能用extern修饰static变量的原因,extern修饰全局变量和函数,在其他文件中是通用的;这也是多个地方引用静态全局变量所在的头文件不会出现重定义错误的原因,因为在每个编译单元都对它开辟了额外的空间进行存储。
- C++中,全局static变量和class的static成员在main之前先初始化,main之后销毁
- 函数内的static变量在什么时候执行初始化?
- 如果初始值是一个编译期常量,在函数的任何一行执行之前就已经初始化了
- 如果初始值不是一个编译期常量,或者静态变量是一个拥有构造函数的对象,在执行期初始化,也就是函数第一次调用时初始化
- static成员变量:
- 类外定义static成员时不能重复static变量,该关键字只出现在类内部的声明中
- 不能在类内部初始化静态成员,相反必须在类的外部定义和初始化每个静态成员
- 类似于全局变量,静态数据成员定义于任何函数之外,因此一旦被定义,就存在于程序的整个生命周期
- 类外定义时需要指定对象的类型,类名,作用域运算符及成员自己的名字
类型转换
- 知识点:
- 任何一个转型动作往往令编译器编译出运行期间执行代码——??
- 显示转型可能发生拷贝——??
- 显示转换会不会抛出异常——待验证
- 转换符:
- static_cast:强迫隐式转换,不包含底层const的任何具有明确定义的类型转换。——底层const也可以转啊,比如
boost::simple_segregated_storage
中的 nextof- 适用场合:编译器隐式执行的任何类型转换都可以由static_cast来完成
a. 需要把一个较大的算术类型赋值给较小的类型
b. 对于编译器无法自动执行的类型转换如:子类(指针)转成基类(指针)但不做运行时的检查,不如dynamic_cast安全。static_cast仅靠类型转换语句中提供的信息来进行转换,而dynamic_cast则会遍历整个类继承体系进行类型检查,因此dynamic_cast在执行效率要差一些。
c. void* 转成具体结构,不同指针类型之间的转换使用static_cast<void*>作为桥梁
d. int转成对应的枚举
e. 将non-const转成const等,但不能将const转成non-const——不需要吧,non-const可以直接给const
f. 针对右值引用的特许转换规则:虽然不能隐式的将一个左值转换为右值引用,但可以用static_cast显式地将一个左值转换为一个右值引用,也就是std::move的功能吧:static_cast<T&&>(t)
- 适用场合:编译器隐式执行的任何类型转换都可以由static_cast来完成
- const_cast:只能改变底层const;因为不能把一个const对象赋给非const对象,通过该函数可进行转换:去const属性;
- 适用场合:——待验证
a. const_cast常常用于有函数重载的上下文中
b. 去const属性
const char * pc;
char * p = const_cast<char*>(pc); //正确但通过p写值是未定义的行为
- 适用场合:——待验证
- reinterpret_cast:通常为运算对象的位模式提供较低层次上的重新解释,执行低级转型,实际动作取决于编译器,这也表示他是不可移植的,转换结构几乎都是在执行期定义。
- 要点:一个指向字符串的指针是如何地与一个指向整数的指针或者一个指向其他自定义类型对象的指针有所不同呢?从内存需求的观点来说并没有不同,它们三个都需要足够的内存来放置一个机器地址。指向不同类型各指针之间的差异,既不在其指针表示法不同,也不在其内容不同,而是其所寻址出来的对象类型不同,也就是说,指针类型会教导编译器如何解释某个特定地址中的内存内容及其大小
- 适用场合:从语法上看,这个操作符仅用于指针类型的转换(返回值是指针)。它用来将一个类型指针转换为另一个类型指针,它只需在编译时重新解释指针的类型。这个操作符基本不考虑转换类型之间是否是相关的。
- 注意:在gcc上大转小不被允许,需要加编译参数:
-fpermissive
- 代码示例:
- static_cast:强迫隐式转换,不包含底层const的任何具有明确定义的类型转换。——底层const也可以转啊,比如
int num = 0x00636261;//用16进制表示32位int,0x61是字符'a'的ASCII码
int * pnum = #
char * pstr = reinterpret_cast<char *>(pnum);
cout << "pnum指针的值: " << pnum << endl;
cout << "pstr指针的值: " << static_cast<void *>(pstr) << endl;//直接输出pstr会输出其指向的字符串,这里的类型转换是为了保证输出pstr的值
// 以上两行输出的地址值是一样的
cout << "pnum指向的内容: " << hex << *pnum << endl; // 输出636261
cout << "pstr指向的内容: " << pstr << endl; // 输出 abc
// 如果将num的值改为64636261,patr输出abcd***(abcd后几个乱码),原因是,pstr会一直输出直到遇到'\0'
// 所以,如果访问到非法地址,会崩溃
- dynamic_cast:支持运行时类型识别,主要被用于安全地沿着类的继承关系向下进行类型转换,将一个基类转换成派生类
- 语法:
A=dynamic_cast<typeA>(B)
(typeA通常含有虚函数) - 该运算符把B转换成typeA类型的对象。TypeA必须是类的指针、类的引用或者void *——只能针对指针或引用进行强转;
- dynamic_cast的转换是在运行时进行的(指向派生类的基类指针可以转成派生类类型,但静态的基类转派生类是不会成功的,同样适用于引用),它的一个好处是会在运行是做类型检查,如果对象的类型不是期望的类型,它会在指针转换的时候返回NULL,在引用转换的时候抛出一个std::bad_cast异常。所以有两个步骤:
- 第一,判断是不是某个类型;
- 第二,是的话就把指针强转成要的类型,不是就返回NULL或抛出异常;
- dynamic_cast一般只在继承类对象的指针之间或引用之间进行类型转换。如果没有继承关系,则被转化的类具有虚函数对象的指针进行转换,不能被用于缺乏虚函数的类型上(more effective c++ 2)——待验证?
- 语法:
- 备注:
- 尽量避免类型转换,特别是dynamic_cast,有两种方法代替:
- 使用容器并直接在其中存储指向derived class的指针
- 使用虚函数代替
- 尽量避免类型转换,特别是dynamic_cast,有两种方法代替:
- 指针类型转换:
const/constexpr
const
编译期和运行时常量,两者没有区分
- const变量:
- const对象必须初始化,而且是在声明的时候初始化,不能分开,不然会报错。其对象一旦创建后就不能再改变,在const类型的对象上只能执行不改变其内容的操作
- 声明时加extern,再定义(定义时可加可不加),这时候包含声明const头文件的多个文件共享同一个const对象
- 如果声明时不加extern直接定义,包含了const变量头文件的编译单元会开辟新的内存存放该变量,类似static变量,不会报链接时重定义,但地址不同
- 初始化对const的引用:允许为一个常量引用绑定非常量的对象,字面值(右值),甚至一般表达式,但数据类型是要一致的;常量引用仅对引用可参与的操作做出了限定,对引用的对象本身是不是常量未做限定,所以允许通过其他途径改变它的值;(常量左值引用是万能的)
const int &r = 42;
正确;常量值(右值)作为实参传递给const T&也是允许的 - 如果用一个对象去初始化另一个对象,则它们是不是const都无关紧要
int i = 42; const int v = i;
可以用非const变量初始化const变量
int j = v;
可以用const变量初始化非const变量,但注意如果变量是指针类型则不允许(常量指针还是指向常量的指针?) - 默认状态下const对象仅在文件内有效
- 初始化指向常量的指针:同初始化对const的引用,允许一个指向常量的指针指向一个非常量对象;指向常量的指针要求不能通过该指针改变对象的值,但没有规定那个对象的值不能通过其他方式改变;
- 不能用指向非常量的指针或引用指向一个常量对象,但可以用非指针或引用的const变量初始化一个非const变量
- 顶层const(常量指针):const作用于对象本身,本身是一个常量;——*const;
- 低层const(指针常量):所指的对象是一个常量;声明引用的const都是低层const
- 执行拷贝操作时,顶层const不受什么影响,拷入和拷出的对象是否是常量都没什么影响;——什么意思
- 底层const:拷入和拷出的对象必须具有相同的底层const资格,或者,两个对象的数据类型必须能够转换,一般是非常量可以转换成常量——?
- const对象必须初始化,而且是在声明的时候初始化,不能分开,不然会报错。其对象一旦创建后就不能再改变,在const类型的对象上只能执行不改变其内容的操作
- const成员函数:
- 默认情况下,this指针是类类型非常量版本的常量指针(如class A,默认this类型是A *const;加上const后的类型为为
const A *const
),这一情况使得不能在一个常量对象上调用普通的成员函数 - 在设计类的时候,一个原则就是对于不改变数据成员的成员函数在后面加 const;不允许修改类的数据成员,但可以修改成员指针变量指向的数据;
- 在类外定义时要加上const,在类中声明方法时可以不带const,在类外定义时可以带const——boost库中simple_segregated_storage::segregate就是这样的,感觉很奇怪
- const对象能调用const成员函数但不能调用非const成员函数,非const对象是可以调用const成员函数的
- const成员函数可以被具有相同形参列表的非const成员函数重载,在这种情况下,类对象的常量属性决定了调用哪个方法——已验证
- 默认情况下,this指针是类类型非常量版本的常量指针(如class A,默认this类型是A *const;加上const后的类型为为
constexpr
编译期常量
- 常量表达式:值不会改变且在编译时就能得到结果的表达式
- 一个对象是不是常量表达式由它的数据类型和初始值共同决定,数据类型是constexpr,初始值必须是常量
- 用常量表达式初始化的const对象也是常量表达式
- 声明constexpr类型的变量一定是一个常量且必须用常量表达式初始化
- 常量表达式的类型要求:
- 字面值类型:算术类型,引用,指针;string/IO/自定义等不是
- 指针和引用的初始值受到严格限制:指针必须是nullptr或0,或存储于某个固定地址中的对象
- 函数体内定义的变量并非存放在固定的地址中,因此不能指向这样的这样的变量;定义在所有函数体外的所有变量地址固定不变,可以用来初始化
- 定义有效范围超出函数本身的变量,这类变量也有固定地址,可以用来初始化,如static变量?
- constexpr定义的指针把定义的对象设置为了顶层const(仅对指针有效,对指针指向的对象无关)
- constexpr修饰变量:const int i=1;和constexpr int j=1;两者在大多数情况下没有区别,但如果i在全局变量中编译期会为i产生数据,对于j,如果没有显式使用编译期不会为其产生数据
const int *p = nullptr;
p是一个指向整型常量的指针
constexpr int *q = nullptr;
q是一个指向整数的常量指针
- constexpr函数:被隐式的指定为内联函数,
- 函数的返回类型及所有形参都是字面值类型,且函数体中有且只有一条return语句(必须返回值)
- 函数体中也可以包含其他语句,只要这些语句在运行时不执行任何操作就行(不执行任何操作有什么用?)如static_assert/using/typedef 等
- 允许constexpr的返回值并非一个常量;所以constexpr函数不一定返回常量表达式
- constexpr构造函数:声明了constexpr的构造函数的类就是字面值常量类了(不知道有啥用)。除了声明为=default或者=delete之外,constexpr构造函数的函数体一般为空,使用初始化列表或其他constexpr构造函数初始化成员
C++中的关键字
- mutable:可变数据成员,在const成员函数内也能改变数据成员,通过在变量的声明中加入mutable。一个mutable数据成员永远不会是const,即使是const对象的成员
- __declspec (novtable ):表示这个类不生成虚函数表,但继承类不受影响;使用此关键字相对节省空间——待验证
- delctype:类型指示符,作用是选择并返回操作数的数据类型
- register:让编译器将变量直接放入寄存器中,以提高存取速度——待验证
- volatile:阻止编译器进行优化
- 用法和const类似,起到对类型额外修饰的作用,只有volatile的成员函数才能被volatile的对象调用
- 只能将volatile对象的地址赋给一个指向volatile的指针,同时只有当某个引用是volatile的时候才能使用一个volatile对象初始化该引用
- const和volatile的一个重要区别是不能使用合成的拷贝/移动构造函数及赋值运算符初始化volatile对象或从volatile对象赋值。合成的成员接收到参数类型是非volatile,不能把一个非volatile引用绑定到一个volatile对象身上。
- 如果一个类希望拷贝,移动或赋值它的volatile对象,则该类必须自定义拷贝或移动操作
class Foo
{
public:
Foo(const volatile Foo&);
Foo& operator=(volatile const Foo&);
Foo & operator=(volatile const Foo&) volatile;
};
union
一种节省空间的类
- 一个union可以有多个数据成员,但在任意时刻只能有一个数据成员可以有值,当给某个成员赋值之后,该union的其他成员就变成未定义状态了。
- 分配一个union的对象的存储空间至少要能容纳它的最大的数据成员
- 和其他类一样,一个union定义了一种新类型
- union不能含有引用类型的成员
- 在C++11新标准中,含有构造和析构的类类型成员也可以作为union的成员类型,union可以为其成员指定public/protected/private等标记,默认情况下union的成员都是公有的
- 匿名union:
基础知识点
- 全局变量和函数:在. h中声明时加上extern,在.cpp中定义(不需要加extern);包括了声明了extern头文件的文件中可以直接使用该变量,如果没有包含头文件,需要加上extern声明
- 不完全类型使用情景:只能是本类型的指针或引用,声明以不完全类型作为参数或返回值的函数;所以类允许包含指向自身的指针或引用
- 单一定义规则(ODR):可以多处声明但只能单一定义;在一个头文件中不要定义,因为C++的编译单位是cpp文件,如果这个头文件被多个cpp文件包含,在.o文件链接的时候会报多处定义的错;如果非要用,可以定义成static变量;或使用extern
- 引用和指针的区别:
1. 引用必须要初始化,且不能为空,指针可以为空——不存在指向空值的引用意味着使用引用的代码效率比使用指针的要高,因为在使用引用前不需要测试它的合法性;相反指针应该总是被测试,防止其为空
2. 引用类型的变量会占用内存空间,占用的内存空间的大小和指针类型的大小是相同的。
3. 引用自始至终只能绑定一个变量,指针可以指向不同的地址
4. sizeof 运算符应用于引用,给出它所引用的元素的大小,所以用sizoef直接计算一个引用变量的大小取到的不是引用本身的大小 - 移位操作:
- 左移相当于乘,左移一位乘以2
- 右移相当于除,右移一位相当于除以2,右移两位相当于除以4
- 对于右移大于或等于位宽的操作,或者右移负数的操作,结果依赖于编译器,不唯一,vs上为原数不变
- C++确保delete一个空指针是安全的(vs实测delete一个NULL指针没事)
- 数组指针和指针数组
- 数组指针(也称行指针):是个指针,指向数组
- 定义 int (*p)[n];
- ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
- 如要将二维数组赋给一指针,应这样赋值:
*int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
- 指针数组:是个数组,里面包含的是指针
- 定义 int *p[n];
- []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值。
- 这两者的区别:数组指针似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。注意:char类型的不管是指针还是数组,都要以"\0"结尾,不然不知道何时结束,导致出现奇怪的值,或者长度和申请的长度不一致
- 数组指针(也称行指针):是个指针,指向数组
- strlen和sizeof的区别:
- strlen计算长度不包含终止null字节的字符串长度,sizeof则计算了包括终止null字节的缓冲区长度
- strlen需要一次函数调用,而对于sizeof而言,因为缓冲区已用已知字符串进行初始化,其长度是固定的,所以sizeof是在编译时计算缓冲区长度
- 编译器会在一个字符串字面常量的末尾插入一个空字符作为终结符
const char *a = "hello";
cout << sizeof(a) << endl; // 输出6
cout << strlen(a) << endl; // 输出5
- 小型对象分配:为什么需要小型对象分配器:
- C++中的new/delete只是对C heap分配器的包装,C heap分配器并未特别对小块内存的分配进行优化,C分配器通常用来分配中大型对象(数百或数千个bytes),所以导致分配速度慢
- C++缺省分配器的通用性也造成小型对象空间分配的低效。缺省分配器管理一个记忆池,而这种管理通常需要耗费一些额外内存,如果分配区块大的话这些开销是微不足道的,但如果分配区块小的话开销所占的比例就非常大了
- 在C++中动态分配很重要,执行期多态和动态分配的联系最为紧密,高效的C++程序开发缺省分配器的低劣性能成为一种障碍
- 输入/输出运算符的重载:输入输出运算符必须是非成员函数,一般声明为友元
- 输出运算符<<:
- 第一个形参是non-const的ostream引用,向流写入内容,所以是non-const,无法复制ostream对象,所以是引用; 第二个形参是一个常量的引用,是要打印的类类型
- 为了与其他输出运算符保持一致,operator<<一般返回它的ostream形参
- 输入运算符>>:
- 第一个形参是运算符将要读取的流的引用,第二个形参是将要读到的非常量对象的引用
- 输出运算符<<:
- 一种for的用法:(范围for循环?)
for (int elem : coll1) {
cout << elem << " ";
}
- float在计算机中的存储方式:
- 转换函数:
- C中整数,浮点数,字符串之间的转换:
- atoi:字符串转整数
- C++中的转换:
- std::stod: 字符串转浮点型
- C中整数,浮点数,字符串之间的转换:
- 数据类型的自动转换规则:
- 若参与运算的数据类型不同,则先转成同一类型,然后进行运算——应该是先计算,再反转
- 转换按数据长度增加的方向进行,以保证精度不降低。
- 所有的浮点运算都是以双精度进行的,即使仅含float单精度运算的表达式
- char和short参与运算时必须先转成int
- 在赋值运算中赋值号两边的数据类型不同时需要把右边的类型转成左边的类型
- 位域
- 一个位域含有一定数量的二进制位,当一个程序需要向其他程序或者硬件设备传递二进制数据时,通常会用到位域
- 位域在内存中的布局与机器无关
- 位域的类型必须是整型或者枚举类型,因为带符号位域的行为是由具体实现决定的,所以在通常情况下使用无符号类型保存一个位域
- 位域的声明形式是在名字之后紧跟一个冒号和一个常量表达式,该表达式用于指定成员所占的二进制位数
- 取地址运算符不能用于位域,因此任何指针都无法指向类的位域
- 通常使用内置的位运算符操作超过1位的位域
- 如果一个类定义了位域成员,则它通常也会定义一组内联的成员函数以检验或设置位域的值:
class File
{
unsigned int mode: 2; // mode占2位
unsigned int modefied: 1;
unsigned int prot_owner: 3;
unsigned int prot_group: 3;
unsigned int prot_world: 3;
public:
enum modes { READ = 01, WRITE = 02, EXECUTE = 03 };
File &open(File::modes m) {
mode |= READ;
// TODO 其他处理
if (m & WRITE) {
return *this;
}
}
inline bool isRead() const { return mode & READ; }
inline void setWrite() { mode |= WRITE; }
};
- 口算二进制:https://blog.csdn.net/devnn/article/details/82597660
编译与函数参数入栈总结
摘自:http://blog.csdn.net/frankiewang008/article/details/7481865
一些常见用法归纳:
- (void)变量名:这是一些变量未曾使用,防止编译器编译时报警告或编不过的用法
- 结构体初始化时变量名前加".":这是指定给哪个变量赋值,可以不用按顺序给结构体成员赋值