类的const的成员函数,表示其不会改变对象(其实是指针this)的状态.
但是有些时候需要在该类函数中对数据成员进行赋值.这个时候就需要用到mutable关键字了
例如:class Demo { public: Demo(){} ~Demo(){} public: bool getFlag() const { m_nAccess++; return m_bFlag; } private: int m_nAccess; bool m_bFlag; }; int main() { return 0; }
class Demo { public: Demo(){} ~Demo(){} public: bool getFlag() const { m_nAccess++; return m_bFlag; } private: mutable int m_nAccess; bool m_bFlag; }; int main() { return 0; }
无论如何,总是会有一小段时间,i会被放在一个寄存器中,因为算术运算只能在寄存器中进行。
一般来说,volatitle关键字适用于行与行之间,而不是放在行内。
我们先来实现一个简单的函数,来观察一下由编译器产生出来的汇编代码中的不足之处,并观察volatile关键字如何修正这个不足之处。
在这个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一种高度浪费CPU时间的循环方法)
代码如下:void getKey(char* pch) { while (*pch == 0) }
; while (*pch == 0) $L27 ; Load the address stored in pch mov eax, DWORD PTR _pch$[ebp] ; Load the character into the EAX register movsx eax, BYTE PTR [eax] ; Compare the value to zero test eax, eax ; If not zero, exit loop jne $L28 jmp $L27 $L28 ;}
;{ ; Load the address stored in pch mov eax, DWORD PTR _pch$[esp-4] ; Load the character into the AL register movsx al, BYTE PTR [eax] ; while (*pch == 0) ; Compare the value in the AL register to zero test al, al ; If still zero, try again je SHORT $L84 ;}
void getKey(volatile char* pch) { while (*pch == 0) }
;{ ; Load the address stored in pch mov eax, DWORD PTR _pch$[esp-4] ; while (*pch == 0) $L84: ; Directly compare the value to zero cmp BYTE PTR [eax], 0 ; If still zero, try again je SHORT $L84 ;}
作用是"禁止隐形构造函数"被用于自动型别转换。
其中比较典型的例子就是容器类型,在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数.
例如:class Array { public: explicit Array(int size); ...... };
__based 关键字使您能够基于指针(作为现有指针的偏移量的指针)声明指针。是基于指针地址的指针。
允许指针被定义为从某一点开始算的32位偏移值(对于64位的环境,是64 位偏移量),而不是内存的绝对位置
typedef struct tagDEMOSTRUCT { int a; char sz[10]; } DEMOSTRUCT, * PDEMOSTRUCT; HANDLE hFileMapping = CreateFileMapping(...); LPVOID lpHead = (LPDWORD)MapViewOfFile(...); DEMOSTRUCT __based(lpHead)* lpDemo;
DEMOSTRUCT只是随便定义的一个结构,用来代表任意的结构.
虽然__based指针使用起来非常容易,但是,你必须在效率上付出一定的代价.每当你用__based指针处理数据,CPU都必须为它加上基地址,才能指向真正的位置.
__thread是GCC内置的线程局部存储设施,存取效率可以和全局变量相比。
__thread变量每一个线程有一份独立实体,各个线程的值互不干扰。
可以用来修饰那些带有全局性且值可能变,但是又不值得用全局变量保护的变量。
__thread使用规则:
不能修饰class类型,因为无法自动调用构造函数和析构函数,不能修饰函数的局部变量或者class的普通成员变量。
只能修饰POD类型(类似整型指针的标量,不带自定义的构造、拷贝、赋值、析构的类型,二进制内容可以任意复制memset,memcpy,且内容可以复原)。
可以用于修饰全局变量,函数内的静态变量,__thread变量值只能初始化为编译器常量(值在编译器就可以确定const int i=5,运行期常量是运行初始化后不再改变const int i=rand()).
#include<iostream> #include<pthread.h> #include<unistd.h> using namespace std; const int i=5; __thread int var=i;//两种方式效果一样 //__thread int var=5; void* worker1(void* arg); void* worker2(void* arg); int main(){ pthread_t pid1,pid2; //__thread int temp=5; //不能修饰函数局部变量 static __thread int temp=10;//修饰函数内的static变量 pthread_create(&pid1,NULL,worker1,NULL); pthread_create(&pid2,NULL,worker2,NULL); pthread_join(pid1,NULL); pthread_join(pid2,NULL); cout<<temp<<endl;//输出10 return 0; } void* worker1(void* arg){ cout<<++var<<endl;//输出 6 } void* worker2(void* arg){ sleep(1);//等待线程1改变var值,验证是否影响线程2 cout<<++var<<endl;//输出6 }
6
6 //可见__thread值线程间互不干扰
10
(1)用在模板定义
标明其后的模板参数是类型参数
例如
template<typename T, typename Y>
T foo(const T& t, const Y& y){//....};
templace<typename T>
class CTest
{
private:
T t;
public:
//...
}
其实,这里最常用的是使用关键字class,而且二者功能完全相同,这里的class和定义类时的class完全是两回事,C++当时就是为了减少关键字,才使用了class。但最终却不得不引入了typename,究竟是
什么原因呢?请看第二条,也就是typename的第二个用法。
(2) 模板中标识“内嵌类型”
这里有三个词,内嵌、依赖、类型名。那么什么是“内嵌依赖类型名(nested dependent type name)”?
请看SGI STL里的一个例子, 只是STL中count范型算法的实现:
template <class _InputIter, class _Tp>
typename iterator_traits<_InputIter>::difference_type
count(_InputIter __first, _InputIter __last, const _Tp& __value) {
__STL_REQUIRES(_InputIter, _InputIterator);
__STL_REQUIRES(typename iterator_traits<_InputIter>::value_type,
_EqualityComparable);
__STL_REQUIRES(_Tp, _EqualityComparable);
typename iterator_traits<_InputIter>::difference_type __n = 0;
for ( ; __first != __last; ++__first)
if (*__first == __value)
++__n;
return __n;
}
这里有三个地方用到了typename:返回值、参数、变量定义。分别是:
typename iterator_traits<_InputIter>::difference_type
typename iterator_traits<_InputIter>::value_type
typename iterator_traits<_InputIter>::difference_type __n = 0;
difference_type, value_type就是依赖于_InputIter(模板类型参数)的类型名。源码如下:
template <class _Iterator>
struct iterator_traits {
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
内嵌是指定义在类名的定义中的。以上difference_type和value_type都是定义在iterator_traits中的。
依赖是指依赖于一个模板参数。typename iterator_traits<_InputIter>::difference_type中difference_type依赖于模板参数_InputIter。
类型名是指这里最终要指出的是个类型名,而不是变量。例如iterator_traits<_InputIter>::difference_type完全有可能是类iterator_traits<_InputIter>类里的一个static对象。
而且当我们这样写的时候,C++默认就是解释为一个变量的。所以,为了和变量区分,必须使用typename告诉编译器。
那么是不是所有的T::type_or_variable, 或者tmpl<T>:type_or_variable都需要使用typename呢?不是,有以下两个例外。
(3)不需要typename的模板中的类型
1)类模板定义中的基类列表template<class T>
class Derived: public Base<T>::XXX
{
...
}
2)类模板定义中的初始化列表
Derived(int x) : Base<T>::xxx(x)
{
...
}
因为编译器知道这里需要的是类型还是变量:1)基类继承列表里是类型,2)构造函数初始化列表里是成员变量