Hi询吴!這里是山幺幺的c++ primer系列杯矩。寫這個系列的初衷是,雖然在學(xué)校學(xué)習(xí)了c++巾表,但總覺得對這門語言了解不深汁掠,因此我想來啃啃著名的c++ primer,并在這里同步記錄我的學(xué)習(xí)筆記集币。由于我啃的是英文版考阱,所以筆記是中英文夾雜的那種。另外由于已有一定的編程基礎(chǔ)鞠苟,所以這個系列不會含有太基礎(chǔ)的知識乞榨,想入門的朋友最好自己啃書嘻嘻~
控制內(nèi)存分配
重載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
- 執(zhí)行步驟
- 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
- 執(zhí)行步驟
- 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当娱;先在成員函數(shù)中找(如果是類的話)吃既,再在global空間中找,最后用library函數(shù)
- library new & delete:可以重載下面的任一版本趾访,重載的函數(shù)必須是類成員函數(shù)或在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;
- 若重載為成員函數(shù):
- 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(因為若基類有虛析構(gòu)函數(shù),要析構(gòu)的對象類型是動態(tài)綁定的)
- 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的內(nèi)存)
- 重載函數(shù)的內(nèi)容:使用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是指針(不一定指向動態(tài)內(nèi)存)云头,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的區(qū)別: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
析構(gòu)
- 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有虛函數(shù)
- 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會被忽略
- 當(dāng)e是引用時猴鲫,returns the type to which the reference refers
- 當(dāng)e是lvalue of a class type that defines at least one virtual function对人,返回的是動態(tài)類型,e會被evaluate拂共;其他情況返回的是靜態(tài)類型牺弄,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類的定義不同俭驮,但一定會包括這些:
- 其他性質(zhì)
- 有public virtual destructor回溺,因為type_info經(jīng)常作為基類
- 沒有默認構(gòu)造函數(shù)
- copy and move constructors and the assignment operators are all defined as deleted
- 我們不能define, copy, or assign objects of type type_info
- 唯一創(chuàng)建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,所以
- 類型轉(zhuǎn)換
- 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登澜,所以指向靜態(tài)成員的指針就是普通指針
- 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;
};
指向數(shù)據(jù)成員的指針
- 定義和初始化
// 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;
- 作為函數(shù)返回值
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;
指向成員函數(shù)的指針
- 聲明和初始化
char (Screen::*pmf2)(Screen::pos, Screen::pos) const; // 聲明
pmf2 = &Screen::get; // 初始化
- 與普通函數(shù)指針的不同: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
- 在與對象綁定前,指向成員函數(shù)的指針并不是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:可以推斷函數(shù)的形參類型和返回類型
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) { }
定義嵌套類的靜態(tài)成員
- 必須寫在enclosing class的scope外
int TextQuery::QueryResult::static_mem = 1024;
name lookup
- a nested class is a nested scope
Union:節(jié)省空間的類
概述
- 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不能繼承&被繼承谴仙,所以也沒有虛函數(shù)
定義&初始化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不能有成員函數(shù))
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
概述
- 定義:定義在函數(shù)體內(nèi)的類
- 特點
- 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
- 不能定義靜態(tài)成員
- 能直接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的權(quán)限
- 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可用來修飾變量/成員函數(shù)
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
- 指向用其他語言寫的函數(shù)的指針必須be declared with the same linkage directive as 它指向的函數(shù)
// 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) { /* ... */ }
- 預(yù)處理器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不支持重載函數(shù)
// 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&);