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
- 执行步骤
- 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
- compiler runs the appropriate constructor to construct the object(s) from the specified initializers
- 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
- 执行步骤
- the appropriate destructor is run on the object to which sp points or on the elements in the array to which arr points
- 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<type*>(e) dynamic_cast<type&>(e) dynamic_cast<type&&>(e)
- type必须是class type,且该class有虚函数
- 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'
- 第一句中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<Derived*>(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<const Derived&>(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 };
- enumerators are constant expressions,所以
- 类型转换
- 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<string*> pvec; function<bool (const string*)> 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<string> 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::set<line_no>>, std::shared_ptr<std::vector<std::string>>);
// other members
};
TextQuery::QueryResult::QueryResult(string s, shared_ptr<set<line_no>> p, shared_ptr<vector<string>> 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 <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
}
- 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&);