第19章 特殊工具与技术
2016-01-26 16:05
471 查看
这些特性在一些特殊应用中非常重要,而在另外一些情况没有什么用,这里介绍这些非广泛使用的特征
new表达式的过程有两个步骤,1调用new操作分配内存,2在内存中执行构造函数
delete表达式也会有两个步骤,1调用对象的析构函数,2调用delete操作收回分配的内润。
自定义的重载只能够针对new操作和delete操作这个步骤。
自定义重载可以增加操作符的参数个数,在使用时,必须使用定位形式,而不是在括号中写入增加的参数。
在重载中需要使用malloc和free进行内容的控制
我们可以将内存分配分解为:
如果在place_address中不使用地址值,则次定位形式将会调用operator new的其他自定义形式分配内存,然后初始化对象。
The typeid operator, which returns the type of a given expression
The dynamic_cast operator, which safely converts a pointer or reference to a base type into a pointer or reference to a derived type
需要保证被转换的类型能够转换成type类型。
如果转换目标是指针类型,失败返回0;
如果是引用类型,失败就会抛出bad_cast异常。
当运算对象不属于类类型或者一个不包含任何虚函数的类时,typeid运算符指示的是运算对象的静态类型。唯有当运算对象是定义了至少一个虚函数的类的左值时,才会知道运行时才会求值。
注意:没有虚函数的类型,会被当做静态类型使用。
不限定作用域的
作用域跟没作用域的区别
默认,枚举值从0开始,依次加1,当然也可以指定专门的值
枚举类型的值是常量表达式
前置声明,无作用域必须有类型
形参匹配与枚举类型
使用数据成员指针
返回数据成员的指针
使用成员函数指针
使用成员指针的类型别名
成员指针函数表
有类中多个相关函数,为了使用更方便,将使用成员指针函数表
加入函数表、枚举表,和一个对成员函数指针使用的别名
使用move
其中有一段代码
if(fp(*it))
fp不是可调用对象,所以会错误
使用function生成一个可调用对象
对于其中调用
if (fcn(*it))
function会转换为
if (((*it).*p)())
使用men_fn生成一个可调用对象
men_fn定义在functional中,可以正确处理类成员函数指针的调用
使用bind生成可调用对象
嵌套类只在作用域中可见,需要注意的是,两个类是相互独立的,一点关系都没有。
C++11扩展了union,可以定义更多成员,但是其管理难度也有了进一步的增加。通常,会在一个类中管理union,在类其中,需要自定义各种构造函数,并且,需要保存一个判别式,指示当前union中存储的对象类型。
需要注意的是,合成的拷贝/移动构造函数、赋值运算符对volatile对象是无效的.
19.1控制内存分配
标准库自定义了如下内存分配工具// theseversions might throw an exception void *operator new(size_t); // allocate an object void *operator new[](size_t); // allocate an array void operator delete(void*) noexcept; // free an object void operator delete[](void*) noexcept; // free an array // versions that promise not to throw; void *operator new(size_t, nothrow_t&) noexcept; void *operator new[](size_t, nothrow_t&) noexcept; void operator delete(void*, nothrow_t&) noexcept; void operator delete[](void*, nothrow_t&) noexcept;
new表达式的过程有两个步骤,1调用new操作分配内存,2在内存中执行构造函数
delete表达式也会有两个步骤,1调用对象的析构函数,2调用delete操作收回分配的内润。
自定义的重载只能够针对new操作和delete操作这个步骤。
自定义重载可以增加操作符的参数个数,在使用时,必须使用定位形式,而不是在括号中写入增加的参数。
在重载中需要使用malloc和free进行内容的控制
void *operatornew(size_t size) { if (void *mem = malloc(size)) return mem; else throw bad_alloc(); } void operator delete(void *mem) noexcept { free(mem); }
19.1.2定位new表达式
当通过地址值调用时定位new表达式,将会使用operator new(size_t, void*)分配内存,然后在这个地址上进行初始化。我们可以将内存分配分解为:
//使用operator new分配内存 auto p = operator new(sizeof(string)); //使用new定位形式初始化对象 auto pp = new(p) string("123"); //析构对象 pp->~string(); //使用operator delete收回内存 operator delete(pp); 定位new的形式有如下几种: new (place_address) type new (place_address) type(initializers) new (place_address) type[size] new (place_address) type[size]{ braced initializer list }
如果在place_address中不使用地址值,则次定位形式将会调用operator new的其他自定义形式分配内存,然后初始化对象。
19.2运行时类型识别
Run-time type identification(RTTI) is provided through two operator:The typeid operator, which returns the type of a given expression
The dynamic_cast operator, which safely converts a pointer or reference to a base type into a pointer or reference to a derived type
19.2.1运算符dynamic_cast
Dynamic_cast有三种形式dynamic_cast<type*>(e) dynamic_cast<type&>(e) dynamic_cast<type&&>(e)
需要保证被转换的类型能够转换成type类型。
如果转换目标是指针类型,失败返回0;
如果是引用类型,失败就会抛出bad_cast异常。
19.2.2运算符typeid
typeid(e)中,e可以是任意表达式或类型的名字。typeid操作的结果是一个常量对象的引用,该对象的类型是标准库类型type_info或type_info的公有派生类型<typeinfo>。当运算对象不属于类类型或者一个不包含任何虚函数的类时,typeid运算符指示的是运算对象的静态类型。唯有当运算对象是定义了至少一个虚函数的类的左值时,才会知道运行时才会求值。
class Base { virtual void Foo(); }; class Derived :public Base { void Foo() override; }; int main() { Derived *d = new Derived; Base *b = d; //true,都是Derived类型 if (typeid(*b) == typeid(*d)) { cout << "same" << endl; } //false,是Derived类型 if (typeid(*b) == typeid(Base)) { cout << "Base" << endl; } }
注意:没有虚函数的类型,会被当做静态类型使用。
19.2.4类type_info
type_info的操作t1==t2 t1!=t2 | 比较t1和t2是否是同一种类型 |
t.name() | 返回C字符串,表示类型名字 |
t.1.before(t2) | 返回bool,表示t1是否位于t2前,编译器依赖 |
19.3枚举类型enumeration
限定作用域的枚举类型是C++11引入的enum class open_modes { input, output, append }; enum struct open_modes { input, output, append };
不限定作用域的
// unscoped enumeration enum color { red, yellow, green }; // unnamed, unscoped enum enum { floatPrec = 6, doublePrec = 10, double_doublePrec = 10 };
作用域跟没作用域的区别
enum color { red, yellow, green }; // unscoped enumeration enum stoplight { red, yellow, green }; // error: redefines enumerators enum class peppers { red, yellow, green }; // ok: enumerators are hidden color eyes = green; // ok: enumerators are in scope for an unscoped enumeration peppers p = green; // error: enumerators from peppers are not in scope // color::greenis in scope but has the wrong type color hair = color::red; // ok: we can explicitly access the enumerators peppers p2 = peppers::red; // ok: using red from peppers
默认,枚举值从0开始,依次加1,当然也可以指定专门的值
enum class intTypes { charTyp = 8, shortTyp = 16, intTyp = 16, longTyp = 32, long_longTyp = 64 };
枚举类型的值是常量表达式
constexpr intTypes charbits = intTypes::charTyp;
前置声明,无作用域必须有类型
// forward declaration of unscoped enum named intValues enum intValues : unsigned long long; // unscoped, must specify a type enum class open_modes; // scoped enums can use int by default
形参匹配与枚举类型
// unscope denumeration; the underlying type is machine dependent enum Tokens { INLINE = 128, VIRTUAL = 129 }; void ff(Tokens); void ff(int); int main() { Tokens curTok = INLINE; ff(128); // exactly matches ff(int) ff(INLINE);// exactly matches ff(Tokens) ff(curTok);// exactly matches ff(Tokens) return 0; }
19.4类成员指针
如下类示例class Screen { public: typedef std::string::size_type pos; char get_cursor() const { return contents[cursor]; } char get() const; char get(pos ht, pos wd) const; private: std::string contents; pos cursor; pos height, width; };
19.4.1数据成员指针
// pdata can point to a string member of a const (or non const) Screen object const string Screen::*pdata; pdata = &Screen::contents; auto p= &Screen::contents;
使用数据成员指针
auto pdata = &Screen::contents; Screen myScreen, *pScreen = &myScreen; // .* dereferences pdata to fetch the contents member from the object myScreen auto s = myScreen.*pdata; // ->* dereferences pdata to fetch contents from the object to which pScreen points s = pScreen->*pdata;
返回数据成员的指针
class Screen { public: static const std::string Screen::*data() { return &Screen::contents; } };
19.4.2成员函数指针
char (Screen::*pmf2)(Screen::pos, Screen::pos) const; pmf2 = &Screen::get; auto pmf = &Screen::get_cursor;
使用成员函数指针
Screen myScreen, *pScreen = &myScreen; // call the function to which pmf points on the object to which pScreen points char c1 = (pScreen->*pmf)(); // passes the arguments 0, 0 to the two-parameter version of get on the object myScreen char c2 = (myScreen.*pmf2)(0, 0);
使用成员指针的类型别名
// Action isa type that can point to a member function of Screen // that returns a char and takes two pos arguments using Action = char (Screen::*)(Screen::pos, Screen::pos) const; Action get = &Screen::get; // get points to the get member of Screen // action takesa reference to a Screen and a pointer to a Screen member function Screen& action(Screen&, Action = &Screen::get); Screen myScreen; // equivalent calls: action(myScreen); // uses the default argument action(myScreen, get); // uses the variable get that we previously defined action(myScreen, &Screen::get); // passes the address explicitly
成员指针函数表
有类中多个相关函数,为了使用更方便,将使用成员指针函数表
class Screen { public: // other interface and implementation members as before Screen&home(); // cursor movement functions Screen&forward(); Screen&back(); Screen&up(); Screen&down(); };
加入函数表、枚举表,和一个对成员函数指针使用的别名
class Screen { public: // other interface and implementation members as before // Action is a pointer that can be assigned any of the cursor movement members using Action = Screen& (Screen::*)(); // specify which direction to move; enum see § 19.3 (p. 832) enum Directions{ HOME, FORWARD, BACK, UP, DOWN }; Screen&move(Directions); private: static Action Menu[]; // function table };
使用move
Screen& Screen::move(Directions cm) { // run the element indexed by cm on this object return(this->*Menu[cm])(); // Menu[cm] points to a member function } Screen::Action Screen::Menu[] = { &Screen::home, &Screen::forward, &Screen::back, &Screen::up, &Screen::down, };
19.4.3将成员函数用作可调用对象
类的成员函数指针必须通过->*或者.*绑定到对象的时候才能够进行,所以成员指针不是一个可调用对象,不能够直接讲一个指向成员函数的指针传递给一个算法:auto fp = &string::empty; // fp points to the string empty function // error: must use .* or ->* to call a pointer to member find_if(svec.begin(), svec.end(), fp);
其中有一段代码
if(fp(*it))
fp不是可调用对象,所以会错误
使用function生成一个可调用对象
function<bool(const string&)> fcn = &string::empty; find_if(svec.begin(), svec.end(), fcn);
对于其中调用
if (fcn(*it))
function会转换为
if (((*it).*p)())
使用men_fn生成一个可调用对象
auto f = mem_fn(&string::empty); // f takes a string or a string* f(*svec.begin()); // ok: passes a string object; f uses .* to call empty f(&svec[0]); // ok: passes a pointer to string; f uses .-> to call empty
men_fn定义在functional中,可以正确处理类成员函数指针的调用
find_if(svec.begin(), svec.end(), mem_fn(&string::empty));
使用bind生成可调用对象
auto f = bind(&string::empty, _1); f(*svec.begin()); // ok: argument is a string f will use .* to call empty f(&svec[0]); // ok: argument is a pointer to string f will use .-> to call empty
19.5嵌套类Nested Classes
class TextQuery { public: class QueryResult; // nested class to be defined later // other members as in § 12.3.2 (p. 487) };
嵌套类只在作用域中可见,需要注意的是,两个类是相互独立的,一点关系都没有。
19.6联合union
union Token { // members are public by default char cval; int ival; double dval; }; Anonymous unions union { // anonymous union char cval; int ival; double dval; }; // defines an unnamed object, whose members we can access directly cval = 'c'; // assigns a new value to the unnamed, anonymous union object ival = 42; // that object now holds the value 42
C++11扩展了union,可以定义更多成员,但是其管理难度也有了进一步的增加。通常,会在一个类中管理union,在类其中,需要自定义各种构造函数,并且,需要保存一个判别式,指示当前union中存储的对象类型。
19.7局部类
在函数中定义的类叫做局部类int a, val; void foo(int val) { static int si; enum Loc{ a = 1024, b }; // Bar is local to foo struct Bar { Loc locVal; // ok: uses a local type name int barVal; void fooBar(Loc l = a) // ok: default argument is Loc::a { barVal = val; // error: val is local to foo barVal = ::val; // ok: uses a global object barVal = si; // ok: uses a static local object locVal = b; // ok: uses an enumerator } }; }
19.8固有不可移植特性Inherently Nonportable Features
19.8.1位域
将类的非静态成员定义成位域typedef unsigned int Bit; class File { Bit mode : 2; // mode has 2 bits Bit modified : 1; // modified has 1 bit Bit prot_owner : 3; // prot_owner has 3 bits Bit prot_group : 3; // prot_group has 3 bits Bit prot_world : 3; // prot_world has 3 bits // operations and data members of File public: // file modes specified as octal literals; see § 2.1.3 (p. 38) enum modes { READ = 01, WRITE = 02, EXECUTE = 03 }; File&open(modes m) { mode |= READ; // set the READ bit by default // other processing if (m & WRITE) // if opening READ and WRITE // processing to open the file in read/write mode return*this; } void close() { if (modified) // . . . save contents } void write() { modified = 1; // . . . } bool isRead() const { return mode & READ; } void setWrite() { mode |= WRITE; } };
19.8.2限定符volatile
程序处理的对象中,其值非程序直接控制,比如系统时钟更新的变量,应给声明为volatile,告诉编译器不对此进行优化。需要注意的是,合成的拷贝/移动构造函数、赋值运算符对volatile对象是无效的.
19.8.3链接提示extern“C”
// illustrative linkage directives that might appear in the C++ header <cstring> // single-statement linkage directive extern "C" size_t strlen(const char *); // compound-statement linkage directive extern "C" { int strcmp(const char*, const char*); char*strcat(char*, const char*); } 还可以包含头文件进去 // compound-statement linkage directive extern "C" { #include <string.h> // C functions that manipulate C-style strings }
相关文章推荐
- openwrt下面创建/添加package----Makefile模版
- meta link之预加载图片、文件
- 最长公共子序列(LCS)
- uva11729
- C++中pair的用法
- Android_RxJava_Demo
- C++中引用(&)的用法
- kcotSlleSdnayuBtoTimetseB.121
- Android 图片手势缩放
- iOS开发——屏幕尺寸适配
- Kconfig、Kbuild Makefile
- linux怎样判断当前登录的用户是从console登录还是telnet登录的
- Nginx HTTP返回状态码修改
- gradlew 打包
- 一种求解组合数的思路
- java读取properties文件
- ios7.2之后的警告汇总
- 从django的中间件直接返回请求
- 37.You issued the following command to drop the PRODUCTS table:
- SSIS Catalog1:View Overview