C語言和C++相關(guān)的面試題一直比較少見一些,沒有 Java方向?qū)懙娜四敲炊?,這是一篇 C 語言與 C++面試知識(shí)點(diǎn)總結(jié)的文章,非常難得。
修飾變量,說明該變量不可以被改變;
修飾指針,分為指向常量的指針(pointer to const)和自身是常量的指針(常量指針,const pointer);
修飾引用,指向常量的引用(reference to const),用于形參類型,即避免了拷貝,又避免了函數(shù)對(duì)值的修改;
修飾成員函數(shù),說明該成員函數(shù)內(nèi)不能修改成員變量。
(為了方便記憶可以想成)被 const 修飾(在 const 后面)的值不可改變,如下文使用例子中的 p2、p3。
// 類
class A
{
private:
const int a; // 常對(duì)象成員,只能在初始化列表賦值
public:
// 構(gòu)造函數(shù)
A() : a(0) { };
A(int x) : a(x) { }; // 初始化列表
// const可用于對(duì)重載函數(shù)的區(qū)分
int getValue(); // 普通成員函數(shù)
int getValue() const; // 常成員函數(shù),不得修改類中的任何數(shù)據(jù)成員的值
};
void function()
{
// 對(duì)象
A b; // 普通對(duì)象,可以調(diào)用全部成員函數(shù)、更新常成員變量
const A a; // 常對(duì)象,只能調(diào)用常成員函數(shù)
const A *p = &a; // 指針變量,指向常對(duì)象
const A &q = a; // 指向常對(duì)象的引用
// 指針
char greeting[] = "Hello";
char* p1 = greeting; // 指針變量,指向字符數(shù)組變量
const char* p2 = greeting; // 指針變量,指向字符數(shù)組常量(const 后面是 char,說明指向的字符(char)不可改變)
char* const p3 = greeting; // 自身是常量的指針,指向字符數(shù)組變量(const 后面是 p3,說明 p3 指針自身不可改變)
const char* const p4 = greeting; // 自身是常量的指針,指向字符數(shù)組常量
}
// 函數(shù)
void function1(const int Var); // 傳遞過來的參數(shù)在函數(shù)內(nèi)不可變
void function2(const char* Var); // 參數(shù)指針?biāo)竷?nèi)容為常量
void function3(char* const Var); // 參數(shù)指針為常量
void function4(const int& Var); // 引用參數(shù)在函數(shù)內(nèi)為常量
// 函數(shù)返回值
const int function5(); // 返回一個(gè)常數(shù)
const int* function6(); // 返回一個(gè)指向常量的指針變量,使用:const int *p = function6();
int* const function7(); // 返回一個(gè)指向變量的常指針,使用:int* const p = function7();
修飾普通變量,修改變量的存儲(chǔ)區(qū)域和生命周期,使變量存儲(chǔ)在靜態(tài)區(qū),在 main 函數(shù)運(yùn)行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統(tǒng)用默認(rèn)值初始化它。
修飾普通函數(shù),表明函數(shù)的作用范圍,僅在定義該函數(shù)的文件內(nèi)才能使用。在多人開發(fā)項(xiàng)目時(shí),為了防止與他人命名空間里的函數(shù)重名,可以將函數(shù)定位為 static。
修飾成員變量,修飾成員變量使所有的對(duì)象只保存一個(gè)該變量,而且不需要生成對(duì)象就可以訪問該成員。
修飾成員函數(shù),修飾成員函數(shù)使得不需要生成對(duì)象就可以訪問該函數(shù),但是在 static 函數(shù)內(nèi)不能訪問非靜態(tài)成員。
this 指針是一個(gè)隱含于每一個(gè)非靜態(tài)成員函數(shù)中的特殊指針。它指向調(diào)用該成員函數(shù)的那個(gè)對(duì)象。
當(dāng)對(duì)一個(gè)對(duì)象調(diào)用成員函數(shù)時(shí),編譯程序先將對(duì)象的地址賦給 this 指針,然后調(diào)用成員函數(shù),每次成員函數(shù)存取數(shù)據(jù)成員時(shí),都隱式使用 this 指針。
當(dāng)一個(gè)成員函數(shù)被調(diào)用時(shí),自動(dòng)向它傳遞一個(gè)隱含的參數(shù),該參數(shù)是一個(gè)指向這個(gè)成員函數(shù)所在的對(duì)象的指針。
this 指針被隱含地聲明為: ClassName const this,這意味著不能給 this 指針賦值;在 ClassName 類的 const 成員函數(shù)中,this 指針的類型為:const ClassName const,這說明不能對(duì) this 指針?biāo)赶虻倪@種對(duì)象是不可修改的(即不能對(duì)這種對(duì)象的數(shù)據(jù)成員進(jìn)行賦值操作);
this 并不是一個(gè)常規(guī)變量,而是個(gè)右值,所以不能取得 this 的地址(不能 &this)。
在以下場(chǎng)景中,經(jīng)常需要顯式引用 this 指針:
inline 使用
// 聲明1(加 inline,建議使用)
inline int functionName(int first, int second,...);
// 聲明2(不加 inline)
int functionName(int first, int second,...);
// 定義
inline int functionName(int first, int second,...) {/****/};
// 類內(nèi)定義,隱式內(nèi)聯(lián)
class A {
int doA() { return 0; } // 隱式內(nèi)聯(lián)
}
// 類外定義,需要顯式內(nèi)聯(lián)
class A {
int doA();
}
inline int A::doA() { return 0; } // 需要顯式內(nèi)聯(lián)
優(yōu)點(diǎn)
Are "inline virtual" member functions ever actually "inlined"?
虛函數(shù)內(nèi)聯(lián)使用
#include <iostream>
using namespace std;
class Base
{
public:
inline virtual void who()
{
cout << "I am Base\n";
}
virtual ~Base() {}
};
class Derived : public Base
{
public:
inline void who() // 不寫inline時(shí)隱式內(nèi)聯(lián)
{
cout << "I am Derived\n";
}
};
int main()
{
// 此處的虛函數(shù) who(),是通過類(Base)的具體對(duì)象(b)來調(diào)用的,編譯期間就能確定了,所以它可以是內(nèi)聯(lián)的,但最終是否內(nèi)聯(lián)取決于編譯器。
Base b;
b.who();
// 此處的虛函數(shù)是通過指針調(diào)用的,呈現(xiàn)多態(tài)性,需要在運(yùn)行時(shí)期間才能確定,所以不能為內(nèi)聯(lián)。
Base *ptr = new Derived();
ptr->who();
// 因?yàn)锽ase有虛析構(gòu)函數(shù)(virtual ~Base() {}),所以 delete 時(shí),會(huì)先調(diào)用派生類(Derived)析構(gòu)函數(shù),再調(diào)用基類(Base)析構(gòu)函數(shù),防止內(nèi)存泄漏。
delete ptr;
ptr = nullptr;
system("pause");
return 0;
}
volatile int i = 10;
斷言,是宏,而非函數(shù)。assert 宏的原型定義在 <assert.h>(C)、
assert() 使用
#define NDEBUG // 加上這行,則 assert 不可用
#include <assert.h>
assert( p != NULL ); // assert 不可用
設(shè)定結(jié)構(gòu)體、聯(lián)合以及類成員變量以 n 字節(jié)方式對(duì)齊
#pragma pack(n) 使用
#pragma pack(push) // 保存對(duì)齊狀態(tài)
#pragma pack(4) // 設(shè)定為 4 字節(jié)對(duì)齊
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop) // 恢復(fù)對(duì)齊狀態(tài)
Bit mode: 2; // mode 占 2 位
類可以將其(非靜態(tài))數(shù)據(jù)成員定義為位域(bit-field),在一個(gè)位域中含有一定數(shù)量的二進(jìn)制位。當(dāng)一個(gè)程序需要向其他程序或硬件設(shè)備傳遞二進(jìn)制數(shù)據(jù)時(shí),通常會(huì)用到位域。
extern "C" 的作用是讓 C++ 編譯器將 extern "C" 聲明的代碼當(dāng)作 C 語言代碼處理,可以避免 C++ 因符號(hào)修飾導(dǎo)致代碼不能和C語言庫(kù)中的符號(hào)進(jìn)行鏈接的問題。
#ifdef __cplusplus
extern "C" {
#endif
void *memset(void *, int, size_t);
#ifdef __cplusplus
}
#endif
// c
typedef struct Student {
int age;
} S;
等價(jià)于
// c
struct Student {
int age;
};
typedef struct Student S; 此時(shí) S 等價(jià)于 struct Student,但兩個(gè)標(biāo)識(shí)符名稱空間不相同。
另外還可以定義與 struct Student 不沖突的 void Student() {}。
由于編譯器定位符號(hào)的規(guī)則(搜索規(guī)則)改變,導(dǎo)致不同于C語言。
1.如果在類標(biāo)識(shí)符空間定義了 struct Student {...};,使用 Student me; 時(shí),編譯器將搜索全局標(biāo)識(shí)符表,Student 未找到,則在類標(biāo)識(shí)符內(nèi)搜索。
即表現(xiàn)為可以使用 Student 也可以使用 struct Student,如下:
// cpp
struct Student {
int age;
};
void f( Student me ); // 正確,"struct" 關(guān)鍵字可省略
2.若定義了與 Student 同名函數(shù)之后,則 Student 只代表函數(shù),不代表結(jié)構(gòu)體,如下:
typedef struct Student {
int age;
} S;
void Student() {} // 正確,定義后 "Student" 只代表此函數(shù)
//void S() {} // 錯(cuò)誤,符號(hào) "S" 已經(jīng)被定義為一個(gè) "struct Student" 的別名
int main() {
Student();
struct Student me; // 或者 "S me";
return 0;
}
總的來說,struct 更適合看成是一個(gè)數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)體,class 更適合看成是一個(gè)對(duì)象的實(shí)現(xiàn)體。
區(qū)別:
最本質(zhì)的一個(gè)區(qū)別就是默認(rèn)的訪問控制
聯(lián)合(union)是一種節(jié)省空間的特殊的類,一個(gè) union 可以有多個(gè)數(shù)據(jù)成員,但是在任意時(shí)刻只有一個(gè)數(shù)據(jù)成員可以有值。當(dāng)某個(gè)成員被賦值后其他成員變?yōu)槲炊x狀態(tài)。聯(lián)合有如下特點(diǎn):
#include<iostream>
union UnionTest {
UnionTest() : i(10) {};
int i;
double d;
};
static union {
int i;
double d;
};
int main() {
UnionTest u;
union {
int i;
double d;
};
std::cout << u.i << std::endl; // 輸出 UnionTest 聯(lián)合的 10
::i = 20;
std::cout << ::i << std::endl; // 輸出全局靜態(tài)匿名聯(lián)合的 20
i = 30;
std::cout << i << std::endl; // 輸出局部匿名聯(lián)合的 30
return 0;
}
C 實(shí)現(xiàn) C++ 的面向?qū)ο筇匦裕ǚ庋b、繼承、多態(tài))
struct A
{
A(int) { }
operator bool() const { return true; }
};
struct B
{
explicit B(int) {}
explicit operator bool() const { return true; }
};
void doA(A a) {}
void doB(B b) {}
int main()
{
A a1(1); // OK:直接初始化
A a2 = 1; // OK:復(fù)制初始化
A a3{ 1 }; // OK:直接列表初始化
A a4 = { 1 }; // OK:復(fù)制列表初始化
A a5 = (A)1; // OK:允許 static_cast 的顯式轉(zhuǎn)換
doA(1); // OK:允許從 int 到 A 的隱式轉(zhuǎn)換
if (a1); // OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
bool a6(a1); // OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
bool a7 = a1; // OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
bool a8 = static_cast<bool>(a1); // OK :static_cast 進(jìn)行直接初始化
B b1(1); // OK:直接初始化
B b2 = 1; // 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以復(fù)制初始化
B b3{ 1 }; // OK:直接列表初始化
B b4 = { 1 }; // 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以復(fù)制列表初始化
B b5 = (B)1; // OK:允許 static_cast 的顯式轉(zhuǎn)換
doB(1); // 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以從 int 到 B 的隱式轉(zhuǎn)換
if (b1); // OK:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象可以從 B 到 bool 的按語境轉(zhuǎn)換
bool b6(b1); // OK:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象可以從 B 到 bool 的按語境轉(zhuǎn)換
bool b7 = b1; // 錯(cuò)誤:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象不可以隱式轉(zhuǎn)換
bool b8 = static_cast<bool>(b1); // OK:static_cast 進(jìn)行直接初始化
return 0;
}
一條 using 聲明 語句一次只引入命名空間的一個(gè)成員。它使得我們可以清楚知道程序中所引用的到底是哪個(gè)名字。如:
using namespace_name::name;
在 C++11 中,派生類能夠重用其直接基類定義的構(gòu)造函數(shù)。
class Derived : Base {
public:
using Base::Base;
/* ... */
};
如上 using 聲明,對(duì)于基類的每個(gè)構(gòu)造函數(shù),編譯器都生成一個(gè)與之對(duì)應(yīng)(形參列表完全相同)的派生類構(gòu)造函數(shù)。生成如下類型構(gòu)造函數(shù):Derived(parms) : Base(args) { }
using 指示 使得某個(gè)特定命名空間中所有名字都可見,這樣我們就無需再為它們添加任何前綴限定符了。如:
using namespace_name name;
盡量少使用 using 指示 污染命名空間
一般說來,使用 using 命令比使用 using 編譯命令更安全,這是由于它只導(dǎo)入了指定的名稱。如果該名稱與局部名稱發(fā)生沖突,編譯器將發(fā)出指示。using編譯命令導(dǎo)入所有的名稱,包括可能并不需要的名稱。如果與局部名稱發(fā)生沖突,則局部名稱將覆蓋名稱空間版本,而編譯器并不會(huì)發(fā)出警告。另外,名稱空間的開放性意味著名稱空間的名稱可能分散在多個(gè)地方,這使得難以準(zhǔn)確知道添加了哪些名稱。
using 使用
盡量少使用 using 指示
using namespace std;
應(yīng)該多使用 using 聲明
int x;
std::cin >> x ;
std::cout << x << std::endl;
或者
using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;
int count = 11; // 全局(::)的 count
class A {
public:
static int count; // 類 A 的 count(A::count)
};
int A::count = 21;
void fun()
{
int count = 31; // 初始化局部的 count 為 31
count = 32; // 設(shè)置局部的 count 的值為 32
}
int main() {
::count = 12; // 測(cè)試 1:設(shè)置全局的 count 的值為 12
A::count = 22; // 測(cè)試 2:設(shè)置類 A 的 count 為 22
fun(); // 測(cè)試 3
return 0;
}
定作用域的枚舉類型
enum class open_modes { input, output, append };
不限定作用域的枚舉類型
enum color { red, yellow, green };
enum { floatPrec = 6, doublePrec = 10 };
decltype 關(guān)鍵字用于檢查實(shí)體的聲明類型或表達(dá)式的類型及值分類。語法:
decltype ( expression )
decltype 使用
// 尾置返回允許我們?cè)趨?shù)列表之后聲明返回類型
template <typename It>
auto fcn(It beg, It end) -> decltype(*beg)
{
// 處理序列
return *beg; // 返回序列中一個(gè)元素的引用
}
// 為了使用模板參數(shù)成員,必須用 typename
template <typename It>
auto fcn2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type
{
// 處理序列
return *beg; // 返回序列中一個(gè)元素的拷貝
}
常規(guī)引用,一般表示對(duì)象的身份。
右值引用就是必須綁定到右值(一個(gè)臨時(shí)對(duì)象、將要銷毀的對(duì)象)的引用,一般表示對(duì)象的值。
右值引用可實(shí)現(xiàn)轉(zhuǎn)移語義(Move Sementics)和精確傳遞(Perfect Forwarding),它的主要目的有兩個(gè)方面:
宏定義可以實(shí)現(xiàn)類似于函數(shù)的功能,但是它終歸不是函數(shù),而宏定義中括弧中的“參數(shù)”也不是真的參數(shù),在宏展開的時(shí)候?qū)?“參數(shù)” 進(jìn)行的是一對(duì)一的替換。
好處
用花括號(hào)初始化器列表初始化一個(gè)對(duì)象,其中對(duì)應(yīng)構(gòu)造函數(shù)接受一個(gè) std::initializer_list 參數(shù).
initializer_list 使用
#include <iostream>
#include <vector>
#include <initializer_list>
template <class T>
struct S {
std::vector<T> v;
S(std::initializer_list<T> l) : v(l) {
std::cout << "constructed with a " << l.size() << "-element list\n";
}
void append(std::initializer_list<T> l) {
v.insert(v.end(), l.begin(), l.end());
}
std::pair<const T*, std::size_t> c_arr() const {
return {&v[0], v.size()}; // 在 return 語句中復(fù)制列表初始化
// 這不使用 std::initializer_list
}
};
template <typename T>
void templated_fn(T) {}
int main()
{
S<int> s = {1, 2, 3, 4, 5}; // 復(fù)制初始化
s.append({6, 7, 8}); // 函數(shù)調(diào)用中的列表初始化
std::cout << "The vector size is now " << s.c_arr().second << " ints:\n";
for (auto n : s.v)
std::cout << n << ' ';
std::cout << '\n';
std::cout << "Range-for over brace-init-list: \n";
for (int x : {-1, -2, -3}) // auto 的規(guī)則令此帶范圍 for 工作
std::cout << x << ' ';
std::cout << '\n';
auto al = {10, 11, 12}; // auto 的特殊規(guī)則
std::cout << "The list bound to auto has size() = " << al.size() << '\n';
// templated_fn({1, 2, 3}); // 編譯錯(cuò)誤!“ {1, 2, 3} ”不是表達(dá)式,
// 它無類型,故 T 無法推導(dǎo)
templated_fn<std::initializer_list<int>>({1, 2, 3}); // OK
templated_fn<std::vector<int>>({1, 2, 3}); // 也 OK
}
面向?qū)ο蟪绦蛟O(shè)計(jì)(Object-oriented programming,OOP)是種具有對(duì)象概念的程序編程典范,同時(shí)也是一種程序開發(fā)的抽象方針。
面向?qū)ο筇卣?/p>
面向?qū)ο笕筇卣?—— 封裝、繼承、多態(tài)
把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對(duì)象操作,對(duì)不可信的進(jìn)行信息隱藏。關(guān)鍵字:public, protected, private。不寫默認(rèn)為 private。
函數(shù)重載
class A
{
public:
void do(int a);
void do(int a, int b);
};
注意:
動(dòng)態(tài)多態(tài)使用
class Shape // 形狀類
{
public:
virtual double calcArea()
{
...
}
virtual ~Shape();
};
class Circle : public Shape // 圓形類
{
public:
virtual double calcArea();
...
};
class Rect : public Shape // 矩形類
{
public:
virtual double calcArea();
...
};
int main()
{
Shape * shape1 = new Circle(4.0);
Shape * shape2 = new Rect(5.0, 6.0);
shape1->calcArea(); // 調(diào)用圓形類里面的方法
shape2->calcArea(); // 調(diào)用矩形類里面的方法
delete shape1;
shape1 = nullptr;
delete shape2;
shape2 = nullptr;
return 0;
}
虛析構(gòu)函數(shù)是為了解決基類的指針指向派生類對(duì)象,并用基類的指針刪除派生類對(duì)象。
虛析構(gòu)函數(shù)使用
class Shape
{
public:
Shape(); // 構(gòu)造函數(shù)不能是虛函數(shù)
virtual double calcArea();
virtual ~Shape(); // 虛析構(gòu)函數(shù)
};
class Circle : public Shape // 圓形類
{
public:
virtual double calcArea();
...
};
int main()
{
Shape * shape1 = new Circle(4.0);
shape1->calcArea();
delete shape1; // 因?yàn)镾hape有虛析構(gòu)函數(shù),所以delete釋放內(nèi)存時(shí),先調(diào)用子類析構(gòu)函數(shù),再調(diào)用基類析構(gòu)函數(shù),防止內(nèi)存泄漏。
shape1 = NULL;
return 0;
}
純虛函數(shù)是一種特殊的虛函數(shù),在基類中不能對(duì)虛函數(shù)給出有意義的實(shí)現(xiàn),而把它聲明為純虛函數(shù),它的實(shí)現(xiàn)留給該基類的派生類去做。
virtual int A() = 0;
虛繼承用于解決多繼承條件下的菱形繼承問題(浪費(fèi)存儲(chǔ)空間、存在二義性)。
底層實(shí)現(xiàn)原理與編譯器相關(guān),一般通過虛基類指針和虛基類表實(shí)現(xiàn),每個(gè)虛繼承的子類都有一個(gè)虛基類指針(占用一個(gè)指針的存儲(chǔ)空間,4字節(jié))和虛基類表(不占用類對(duì)象的存儲(chǔ)空間)(需要強(qiáng)調(diào)的是,虛基類依舊會(huì)在子類里面存在拷貝,只是僅僅最多存在一份而已,并不是不在子類里面了);當(dāng)虛繼承的子類被當(dāng)做父類繼承時(shí),虛基類指針也會(huì)被繼承。
實(shí)際上,vbptr 指的是虛基類表指針(virtual base table pointer),該指針指向了一個(gè)虛基類表(virtual table),虛表中記錄了虛基類與本類的偏移地址;通過偏移地址,這樣就找到了虛基類成員,而虛繼承也不用像普通多繼承那樣維持著公共基類(虛基類)的兩份同樣的拷貝,節(jié)省了存儲(chǔ)空間。
虛繼承
虛函數(shù)
抽象類:含有純虛函數(shù)的類
接口類:僅含有純虛函數(shù)的抽象類
聚合類:用戶可以直接訪問其成員,并且具有特殊的初始化語法形式。滿足如下特點(diǎn):
用于分配、釋放內(nèi)存
malloc、free 使用
申請(qǐng)內(nèi)存,確認(rèn)是否申請(qǐng)成功
char *str = (char*) malloc(100);
assert(str != nullptr);
釋放內(nèi)存后指針置空
free(p);
p = nullptr;
new、delete 使用
申請(qǐng)內(nèi)存,確認(rèn)是否申請(qǐng)成功
int main()
{
T* t = new T(); // 先內(nèi)存分配 ,再構(gòu)造函數(shù)
delete t; // 先析構(gòu)函數(shù),再內(nèi)存釋放
return 0;
}
定位 new(placement new)允許我們向 new 傳遞額外的地址參數(shù),從而在預(yù)先指定的內(nèi)存區(qū)域創(chuàng)建對(duì)象。
new (place_address) type
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] { braced initializer list }
合法,但:
方法:將析構(gòu)函數(shù)設(shè)置為私有
原因:C++ 是靜態(tài)綁定語言,編譯器管理?xiàng)I蠈?duì)象的生命周期,編譯器在為類對(duì)象分配??臻g時(shí),會(huì)先檢查類的析構(gòu)函數(shù)的訪問性。若析構(gòu)函數(shù)不可訪問,則不能在棧上創(chuàng)建對(duì)象。
方法:將 new 和 delete 重載為私有
原因:在堆上生成對(duì)象,使用 new 關(guān)鍵詞操作,其過程分為兩階段:第一階段,使用 new 在堆上尋找可用內(nèi)存,分配給對(duì)象;第二階段,調(diào)用構(gòu)造函數(shù)生成對(duì)象。將 new 操作設(shè)置為私有,那么第一階段就無法完成,就不能夠在堆上生成對(duì)象。
頭文件:#include <memory>
std::auto_ptr<std::string> ps (new std::string(str));
對(duì)于該論述,歡迎讀者查閱之前發(fā)過的文章,你是《未來世界的幸存者》么?
可以在一行中用三個(gè)以上的減號(hào)來建立一個(gè)分隔線,同時(shí)需要在分隔線的上面空一行。如下:
shared_ptr
多個(gè)智能指針可以共享同一個(gè)對(duì)象,對(duì)象的最末一個(gè)擁有著有責(zé)任銷毀對(duì)象,并清理與該對(duì)象相關(guān)的所有資源。
weak_ptr
weak_ptr 允許你共享但不擁有某對(duì)象,一旦最末一個(gè)擁有該對(duì)象的智能指針失去了所有權(quán),任何 weak_ptr 都會(huì)自動(dòng)成空(empty)。因此,在 default 和 copy 構(gòu)造函數(shù)之外,weak_ptr 只提供 “接受一個(gè) shared_ptr” 的構(gòu)造函數(shù)。
unique_ptr
unique_ptr 是 C++11 才開始提供的類型,是一種在異常時(shí)可以幫助避免資源泄漏的智能指針。采用獨(dú)占式擁有,意味著可以確保一個(gè)對(duì)象和其相應(yīng)的資源同一時(shí)間只被一個(gè) pointer 擁有。一旦擁有著被銷毀或編程 empty,或開始擁有另一個(gè)對(duì)象,先前擁有的那個(gè)對(duì)象就會(huì)被銷毀,其任何相應(yīng)資源亦會(huì)被釋放。
auto_ptr
被 c++11 棄用,原因是缺乏語言特性如 “針對(duì)構(gòu)造和賦值” 的 std::move 語義,以及其他瑕疵。
auto_ptr 與 unique_ptr 比較
bad_cast 使用
try {
Circle& ref_circle = dynamic_cast<Circle&>(ref_shape);
}
catch (bad_cast b) {
cout << "Caught: " << b.what();
}
typeid、type_info 使用
#include <iostream>
using namespace std;
class Flyable // 能飛的
{
public:
virtual void takeoff() = 0; // 起飛
virtual void land() = 0; // 降落
};
class Bird : public Flyable // 鳥
{
public:
void foraging() {...} // 覓食
virtual void takeoff() {...}
virtual void land() {...}
virtual ~Bird(){}
};
class Plane : public Flyable // 飛機(jī)
{
public:
void carry() {...} // 運(yùn)輸
virtual void takeoff() {...}
virtual void land() {...}
};
class type_info
{
public:
const char* name() const;
bool operator == (const type_info & rhs) const;
bool operator != (const type_info & rhs) const;
int before(const type_info & rhs) const;
virtual ~type_info();
private:
...
};
void doSomething(Flyable *obj) // 做些事情
{
obj->takeoff();
cout << typeid(*obj).name() << endl; // 輸出傳入對(duì)象類型("class Bird" or "class Plane")
if(typeid(*obj) == typeid(Bird)) // 判斷對(duì)象類型
{
Bird *bird = dynamic_cast<Bird *>(obj); // 對(duì)象轉(zhuǎn)化
bird->foraging();
}
obj->land();
}
int main(){
Bird *b = new Bird();
doSomething(b);
delete b;
b = nullptr;
return 0;
}
來源:https://github.com/huihut/interview#effective
版權(quán)申明:內(nèi)容來源網(wǎng)絡(luò),版權(quán)歸原創(chuàng)者所有。除非無法確認(rèn),都會(huì)標(biāo)明作者及出處,如有侵權(quán)煩請(qǐng)告知,我們會(huì)立即刪除并致歉。謝謝!
聯(lián)系客服