您的位置:首页 > 其它

第19章 特殊工具与技术

2016-01-26 16:05 471 查看
这些特性在一些特殊应用中非常重要,而在另外一些情况没有什么用,这里介绍这些非广泛使用的特征

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
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: