第19章 特殊工具与技术

这些特性在一些特殊应用中非常重要,而在另外一些情况没有什么用,这里介绍这些非广泛使用的特征

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;

 

  1. new表达式的过程有两个步骤,1调用new操作分配内存,2在内存中执行构造函数
  2. delete表达式也会有两个步骤,1调用对象的析构函数,2调用delete操作收回分配的内润。
  3. 自定义的重载只能够针对new操作和delete操作这个步骤。
  4. 自定义重载可以增加操作符的参数个数,在使用时,必须使用定位形式,而不是在括号中写入增加的参数。
  5. 在重载中需要使用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:

  1. The typeid operator, which returns the type of a given expression
  2. 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类型。

  1. 如果转换目标是指针类型,失败返回0;
  2. 如果是引用类型,失败就会抛出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
}
原文地址:https://www.cnblogs.com/qiusuo/p/5160726.html