C++ primer 第十九章-特殊工具与技巧

Hi!这里是山幺幺的c++ primer系列。写这个系列的初衷是,虽然在学校学习了c++,但总觉得对这门语言了解不深,因此我想来啃啃著名的c++ primer,并在这里同步记录我的学习笔记。由于我啃的是英文版,所以笔记是中英文夹杂的那种。另外由于已有一定的编程基础,所以这个系列不会含有太基础的知识,想入门的朋友最好自己啃书嘻嘻~

控制内存分配


重载new和delete

  • new expression
    • operator new function is used when we allocate an object; operator new[] is called when we allocate an array
    • 栗子
    string *sp = new string("a value"); // allocate and initialize a string
    string *arr = new string[10]; // allocate ten default initialized strings
    
    • 执行步骤
      1. the expression calls a library function named operator new (or operator new[]),这个方程会allocates raw, untyped memory large enough to hold an object (or an array of objects) of the specified type
      2. compiler runs the appropriate constructor to construct the object(s) from the specified initializers
      3. a pointer to the newly allocated and constructed object is returned
  • delete expression
    • 栗子
    delete sp; // destroy *sp and free the memory to which sp points
    delete [] arr; // destroy the elements in the array and free the memory
    
    • 执行步骤
      1. the appropriate destructor is run on the object to which sp points or on the elements in the array to which arr points
      2. the compiler frees the memory by calling a library function named operator delete or operator delete[]
  • 通过重载operator new and operator delete functions来重载new和delete:when the compiler sees a new or delete expression, it looks for the corresponding operator function to call;先在成员函数中找(如果是类的话),再在global空间中找,最后用library函数
  • library new & delete:可以重载下面的任一版本,重载的函数必须是类成员函数或在global scope
// these versions might throw an exception (delete肯定不会抛出异常)
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;
  • 若重载为成员函数:
    • these operator functions are implicitly static:the member new and delete functions must be static because they are used either before the object is constructed (operator new) or after it has been destroyed (operator delete)
    • operator delete or operator delete[] may have a second parameter of type size_t,它被initialized with the size in bytes of the object addressed by the first parameter(因为若基类有虚析构函数,要析构的对象类型是动态绑定的)
  • operator new or operator new[]的要求:
    • 返回类型是void*
    • first parameter must have type size_t 且不能有默认值
    • 不能定义它(为library所保留):void operator new(size_t, void);
  • operator delete or operator delete[]的要求:
    • 返回类型是void
    • first parameter must have type void* (指向要free的内存)
  • 重载函数的内容:使用malloc and free
    • malloc:takes a size_t that says how many bytes to allocate;returns a pointer to the memory that it allocated, or 0 if it was unable to allocate
    • free:takes a void* that is a copy of a pointer that was returned from malloc;returns the associated memory to the system;free(0) has no effect
    • 栗子
    void *operator new(size_t size) {
      if (void *mem = malloc(size))
        return mem;
      else
        throw bad_alloc();
    }
    void operator delete(void *mem) noexcept { free(mem); }
    

placement new expression

  • 格式:place_address是指针(不一定指向动态内存),initializers用来construct the newly allocated object
new (place_address) type
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] { braced initializer list }
  • When passed a single argument that is a pointer, a placement new expression constructs an object but does not allocate memory
  • 与allocator的construct的区别:the pointer that we pass to construct must point to space allocated by the same allocator object;the pointer that we pass to placement new need not point to memory allocated by operator new

析构

  • like calling destroy, calling a destructor destroys an object but does not free the memory
string *sp = new string("a value"); // allocate and initialize a string
sp->~string();

Run-Time Type Identification(RTTI)


概述

  • RTTI operators:
    • typeid:returns the type of a given expression
    • dynamic_cast:safely converts a pointer or reference to a base type into a pointer or reference to a derived type
  • when applied to pointers or references to types that have virtual functions, RTTI operators use the dynamic type of the object to which the pointer or reference is bound
  • 使用场景:I f we cannot use a virtual, we can use one of the RTTI operators

dynamic_cast

  • 格式
    dynamic_cast(e)
    dynamic_cast(e)
    dynamic_cast(e)
    
    1. type必须是class type,且该class有虚函数
    2. e的类型必须是其中一种:① a class type that is publicly derived from the target 'type';② a public base class of the target 'type';③ the same as the target 'type'
    3. 第一句中e必须是指针,第二句中e必须是左值,第三局中e必须是右值
  • If a dynamic_cast to a pointer type fails, the result is 0. If a dynamic_cast to a reference type fails, the operator throws an exception of type bad_cast
  • we can do a dynamic_cast on a null pointer; the result is a null pointer of the requested type
  • 栗子
// cast to 指针
if (Derived *dp = dynamic_cast(bp)) {
  // use the Derived object to which dp points
} else { // bp points at a Base object
  // use the Base object to which bp points
}

// cast to 引用
void f(const Base &b) {
  try {
    const Derived &d = dynamic_cast(b);
    // use the Derived object to which b referred
  } catch (bad_cast) {
    // handle the fact that the cast failed
  }
}

typeid

  • 返回值:a reference to a const object of a library type named type_info, or a type publicly derived from type_info
  • typeid(e):
    • e的top-level const会被忽略
    • 当e是引用时,returns the type to which the reference refers
    • 当e是lvalue of a class type that defines at least one virtual function,返回的是动态类型,e会被evaluate;其他情况返回的是静态类型,e不会被evaluate(因为compiler knows the static type without evaluating the expression)
      • p是nullptr时:若p指向的类型是有virtual的class,则typeid(p)会抛出bad_typeid异常,因为p在运行时被evaluate了;若p指向的是其他类型,则typeid(*p)是合法的
  • 栗子:第二句话后,bp是Base*类型,但指向a Derived object的base-part;dp是Derived类型
Derived *dp = new Derived;
Base *bp = dp; // both pointers point to a Derived object
// compare the type of two objects at run time
if (typeid(*bp) == typeid(*dp)) {
  // if bp and dp point to objects of the same type
}
// test whether the run-time type is a specific type
if (typeid(*bp) == typeid(Derived)) {
  // if bp actually points to a Derived
}

type_info类

  • 不同编译器中type_info类的定义不同,但一定会包括这些:
  • 其他性质
    • 有public virtual destructor,因为type_info经常作为基类
    • 没有默认构造函数
    • copy and move constructors and the assignment operators are all defined as deleted
    • 我们不能define, copy, or assign objects of type type_info
    • 唯一创建type_info对象的方式:typeid operator
  • 栗子:注意不同编译器打出的名字不同
int arr[10];
Derived d;
Base *p = &d;
cout << typeid(42).name() << ", "
  << typeid(arr).name() << ", "
  << typeid(Sales_data).name() << ", "
  << typeid(std::string).name() << ", "
  << typeid(p).name() << ", "
  << typeid(*p).name() << endl;

枚举


概述

  • 和类一样,每个枚举都定义了一个新类型
  • enumerations are literal types
  • enumerator:即{}中的那些,比如下面代码中的input、output、append、red、yellow等
  • 分类
    • scoped
    enum class open_modes {input, output, append}; // 或者
    enum struct open_modes {input, output, append};
    
    • unscoped
    enum color {red, yellow, green}; // unscoped enumeration
    // unnamed, unscoped enum
    enum {floatPrec = 6, doublePrec = 10, double_doublePrec = 10};
    

enumerator

  • 可见性
    • scoped enumeration中的enumerators are inaccessible outside the scope of the enumeration
    • unscoped enumeration中的enumerators are placed into the same scope as the enumeration itself
    • 栗子
    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::green is 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
    
    • enumerators are constant expressions,所以
      • if initialized, their initializers must be constant expressions
      • we can use an enum as the expression in a switch statement and use the value of its enumerators as the case labels
      • we can also use an enumeration type as a nontype template parameter
      • we can initialize class static data members of enumeration type inside the class definition
    • 默认情况下,enumerator values start at 0 and each enumerator has a value 1 greater than the preceding one
    • 也可以自定义初始值
    enum class intTypes {
       charTyp = 8, shortTyp = 16, intTyp = 16,
       longTyp = 32, long_longTyp = 64
    };
    
  • 类型转换
    • objects or enumerators of an unscoped enumeration type are automatically converted to an integral type
    int i = color::red; // ok: unscoped enumerator implicitly converted to int
    int j = peppers::red; // error: scoped enumerations are not implicitly converted
    

枚举型对象

  • 只要枚举类型有名字,就可以定义该类型的对象
  • 枚举型对象may be initialized or assigned only by one of its enumerators or by another object of the same enum type
open_modes om = 2; // error: 2 is not of type open_modes
om = open_modes::input; // ok: input is an enumerator of open_modes

specifying the size of an enum

enum intValues : unsigned long long {
  charTyp = 255, shortTyp = 65535, intTyp = 65535,
  longTyp = 4294967295UL,
  long_longTyp = 18446744073709551615ULL
};

PS:不指定的话,scoped enum默认用int,unscoped没有默认用的类型(每个机器不同)
PS:it is an error for an enumerator to have a value that is too large to fit in that type(包括默认用int的情况)

forward declare an enum

  • c++ 11新特性
  • enum forward declaration must specify (implicitly or explicitly) the underlying size of the enum
// 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

栗子

// unscoped enumeration; 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;
}

指向类成员的指针


概述

  • 指向类成员的指针即a pointer that can point to a nonstatic member of a class:因为static class members are not part of any object,所以指向静态成员的指针就是普通指针
  • type of a pointer to member embodies both the type of a class and the type of a member of that class
  • 以下会用到的类
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;
};

指向数据成员的指针

  • 定义和初始化
// pdata can point to a string member of a const (or non const) Screen object
const string Screen::*pdata;
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:
  // data is a static member that returns a pointer to member
  static const std::string Screen::*data() { return &Screen::contents; }
  // other members as before
};

// data() returns a pointer to the contents member of class Screen
const string Screen::*pdata = Screen::data();
// fetch the contents of the object named myScreen
auto s = myScreen.*pdata;

指向成员函数的指针

  • 声明和初始化
char (Screen::*pmf2)(Screen::pos, Screen::pos) const; // 声明
pmf2 = &Screen::get; // 初始化
  • 与普通函数指针的不同:no automatic conversion between a member function and a pointer to that member
// pmf points to a Screen member that takes no arguments and returns char
pmf = &Screen::get; // must explicitly use the address-of operator
pmf = Screen::get; // error: no conversion to pointer for member functions
  • 用法
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);
  • type aliases
// Action is a 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 takes a 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
  • 用作Callable Objects
    • 在与对象绑定前,指向成员函数的指针并不是callable
    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);
    
    • 法一:用function
    vector pvec;
    function fp = &string::empty;
    // fp takes a pointer to string and uses the ->* to call empty
    find_if(pvec.begin(), pvec.end(), fp);
    
    • 法二:用mem_fn:可以推断函数的形参类型和返回类型
    vector svec;
    find_if(svec.begin(), svec.end(), mem_fn(&string::empty));
    
    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
    
    • 法三:用bind
    // bind each string in the range to the implicit first argument to empty
    auto it = find_if(svec.begin(), svec.end(), bind(&string::empty, _1));
    
    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
    

嵌套类


概述

  • objects of the enclosing and nested classes are independent from each other(即object of the nested(enclosing) type does not have members defined by the enclosing(nested) class;且enclosing(nested) class has no special access to the members of a nested(enclosing) class)
  • 可见性:name of a nested class is visible within its enclosing class scope but not outside the class
  • a nested class defines a type member in its enclosing class

声明+在enclosing class外定义嵌套类

  • 因为嵌套类是enclosing class的type member,所以在使用嵌套类之前必须声明它
  • 栗子
class TextQuery {
public:
  class QueryResult; // nested class to be defined later
  // other members
};

// we're defining the QueryResult class that is a member of class TextQuery
class TextQuery::QueryResult {
  // in class scope, we don't have to qualify the name of the QueryResult parameters
  friend std::ostream&
  print(std::ostream&, const QueryResult&);
public:
  // no need to define QueryResult::line_no; a nested class can use a member
  // of its enclosing class without needing to qualify the member's name
  QueryResult(std::string, std::shared_ptr>, std::shared_ptr>);
  // other members
};

TextQuery::QueryResult::QueryResult(string s, shared_ptr> p, shared_ptr> f)
: sought(s), lines(p), file(f) { }

定义嵌套类的静态成员

  • 必须写在enclosing class的scope外
int TextQuery::QueryResult::static_mem = 1024;

name lookup

  • a nested class is a nested scope

Union:节省空间的类


概述

  • a union may have multiple data members, but at any point in time, only one of the members may have a value
  • union所占空间大小:at least as much as is needed to contain its largest data member
  • union的成员不能是引用
  • 默认情况下,members of a union are public
  • union may define member functions, including constructors and destructors
  • union不能继承&被继承,所以也没有虚函数

定义&初始化Union

  • 栗子:有名union(if an initializer is present, it is used to initialize the first member)
// objects of type Token have a single member, which could be of any of the listed types
union Token {
  // members are public by default
  char cval;
  int ival;
  double dval;
};

Token first_token = {'a'}; // initializes the cval member(初始化第一个元素)
Token last_token; // uninitialized Token object
Token *pt = new Token; // pointer to an uninitialized Token object

last_token.cval = 'z';
pt->ival = 42;
  • 栗子:无名union(members of an anonymous union are directly accessible in the scope where the anonymous union is defined;无名union不能有private/protected成员;无名union不能有成员函数)
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

unions with Members of Class Type

  • when we switch the union’s value to and from a member of class type, we must construct or destroy that member
  • when a union has members of built-in type, the compiler will synthesize the memberwise versions of the default constructor or copy-control members;但若 union 有一个类,它定义了 one of these members, the compiler synthesizes the corresponding member of the union as deleted
  • if a class has a union member that has a deleted copy-control member, then that corresponding copy-control operation(s) of the class itself will be deleted as well

Local Classes


概述

  • 定义:定义在函数体内的类
  • 特点
    • visible only in the scope in which it is defined
    • all members, including functions, of a local class must be completely defined inside the class body
    • 不能定义静态成员
    • 能直接access的:type names, static variables, and enumerators defined within the enclosing local scopes
    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
        }
      };
      // . . .
    }
    

enclosing function的权限

  • enclosing function has no special access privileges to the private members of the local class

name lookup

  • if a name is not found as a class member, then the search continues in the enclosing scope and then out to the scope enclosing the function itself

nested local class

void foo() {
  class Bar {
  public:
  // ...
    class Nested; // declares class Nested
  };
  // definition of Nested
  class Bar::Nested {
  // ...
  };
}

Inherently Nonportable Features


定义

  • machine specific features

the sizes of the arithmetic types vary across machines

memory layout of a bit-field is machine dependent

P1045-1047

the precise meaning of volatile is inherently machine dependent

  • volatile的含义:an object should be declared volatile when its value might be changed in ways outside the control or detection of the program(比如program might contain a variable updated by the system clock)
  • volatile的作用:告诉编译器不要 perform optimizations on such objects
  • volatile可用来修饰变量/成员函数
volatile int display_register; // int value that might change
volatile Task *curr_task; // curr_task points to a volatile object
volatile int iax[max_size]; // each element in iax is volatile
volatile Screen bitmapBuf; // each member of bitmapBuf is volatile
  • only volatile member functions may be called on volatile objects
  • volatile与指针:we may assign the address of a volatile object (or copy a pointer to a volatile type) only to a pointer to volatile;we may use a volatile object to initialize a reference only if the reference is volatile
volatile int v; // v is a volatile int
int *volatile vip; // vip is a volatile pointer to int
volatile int *ivp; // ivp is a pointer to volatile int
// vivp is a volatile pointer to volatile int
volatile int *volatile vivp;
int *ip = &v; // error: must use a pointer to volatile
*ivp = &v; // ok: ivp is a pointer to volatile
vivp = &v; // ok: vivp is a volatile pointer to volatile
  • synthesized copy/move and assignment operators cannot be used to initialize or assign from a volatile object:因为synthesized members take parameters that are references to (nonvolatile) const

Linkage Directives: extern "C"

  • C+ + uses linkage directives to indicate the language used for any non-C+ + function
  • linkage directives may not appear inside a class or function definition
  • 对compound-statement linkage directive而言:the names of functions declared within the braces are visible as if the functions were declared outside the braces
  • 栗子
// illustrative linkage directives that might appear in the C++ header 
// 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  // C functions that manipulate C-style strings
}
  • the language in which a function is written is part of its type
void (*pf1)(int); // points to a C++ function
extern "C" void (*pf2)(int); // points to a C function
pf1 = pf2; // error: pf1 and pf2 have different types
  • 指向用其他语言写的函数的指针必须be declared with the same linkage directive as 它指向的函数
// pf points to a C function that returns void and takes an int
extern "C" void (*pf)(int);
  • linkage directive applies to the function and any function pointers used as the return type or as a parameter type
// f1 is a C function; its parameter is a pointer to a C function
extern "C" void f1(void(*)(int));

所以如果想要pass a pointer to a C function to a C+ + function,需要用type alias:

// FC is a pointer to a C function
extern "C" typedef void FC(int);
// f2 is a C++ function with a parameter that is a pointer to a C function
void f2(FC *);
  • Exporting Our C+ + Functions to Other Languages:编译器在处理下面的代码时会generate code appropriate to the indicated language
// the calc function can be called from C programs
extern "C" double calc(double dparm) { /* ... */ }
  • 预处理器allow the same source file to be compiled under either C or C+ +
#ifdef __cplusplus
// ok: we're compiling C++
extern "C"
#endif
int strcmp(const char*, const char*);
  • C不支持重载函数
// error: two extern "C" functions with the same name
extern "C" void print(const char*);
extern "C" void print(int);

// the C function can be called from C and C++ programs
// the C++ functions overload that function and are callable from C++
extern "C" double calc(double);
extern SmallInt calc(const SmallInt&);
extern BigNum calc(const BigNum&);

你可能感兴趣的:(C++ primer 第十九章-特殊工具与技巧)