C++面向?qū)ο蟾呒?jí)編程 part2
@(boolan C++)[C++]
2017-10-22 14:17:56 / helingchao
概述
本章節(jié)內(nèi)容主要講述如何設(shè)計(jì)class with pointer畴博,以string為例涉瘾。
#ifndef __MYSTRING__
#define __MYSTRING__
class String
{
public:
String(const char* cstr=0);
String(const String& str);
String& operator=(const String& str);
~String();
char* get_c_str() const { return m_data; }
private:
char* m_data;
};
#include <cstring>
inline
String::String(const char* cstr)
{
if (cstr) {
m_data = new char[strlen(cstr)+1];
strcpy(m_data, cstr);
}
else {
m_data = new char[1];
*m_data = '\0';
}
}
inline
String::~String()
{
delete[] m_data;
}
inline
String& String::operator=(const String& str)
{
if (this == &str)
return *this;
delete[] m_data;
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
return *this;
}
inline
String::String(const String& str)
{
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
#include <iostream>
using namespace std;
ostream& operator<<(ostream& os, const String& str)
{
os << str.get_c_str();
return os;
}
#endif
big three
所謂big three即:
- 拷貝構(gòu)造
- 拷貝賦值
- 析構(gòu)
1. 編譯器提供的默認(rèn)函數(shù)
聲明一個(gè)空類
聲明一個(gè)空類,如下:
class Empty {
}
C++會(huì)為上面定義的Empty類,提供默認(rèn)的成員出革。效果等同于如下定義:
class Empty {
public:
Empty() {...};
~Empty(){...};
Empty(const Empty& rhs){...};
Empty& operatpr = (const Empty& rhs) {...};
}
C++提供的默認(rèn)函數(shù)
- default 構(gòu)造函數(shù)
- copy構(gòu)造函數(shù)
- 析構(gòu)函數(shù)
- copy assignment函數(shù)
原則 01: C++默認(rèn)提供的函數(shù)僅在被調(diào)用時(shí),編譯器才會(huì)創(chuàng)建它們渡讼。
注意:這四個(gè)函數(shù)都是public和inline的骂束。
默認(rèn)函數(shù)的行為都是什么
default構(gòu)造和析構(gòu)函數(shù)
default 構(gòu)造函數(shù)和析構(gòu)函數(shù)主要是給編譯器一個(gè)地方放置“藏身幕后”的代碼。
- default構(gòu)造函數(shù) :調(diào)用base classes和non-static 成員變量的構(gòu)造函數(shù)成箫。
- 析構(gòu)函數(shù) :調(diào)用base classes和non-static 成員變量的析構(gòu)函數(shù)展箱。
注意: non-static成員的說(shuō)法,static 類成員不在default構(gòu)造函數(shù)的初始化范圍內(nèi)蹬昌。
copy構(gòu)造函數(shù)和copy assignment
單純的將對(duì)象的每一個(gè)non-static成員拷貝到目標(biāo)對(duì)象混驰。
用戶需要重新定義copy構(gòu)造函數(shù)和copy assignment的場(chǎng)景
類中包含以下類型成員
- 引用類型
- 指針類型
- const類型
2. 字符串設(shè)計(jì)
- 用指針保存存儲(chǔ)數(shù)據(jù)的地址,不用數(shù)組皂贩。好處在于靈活的存儲(chǔ)栖榨,無(wú)需考慮設(shè)計(jì)多大的數(shù)組。
實(shí)際是一種動(dòng)態(tài)存儲(chǔ)思想與靜態(tài)存儲(chǔ)思想之間的決策明刷。
3. ctor & dtor
inline string::string(const char* cstr= 0) {
if (cstr) {
m_data = new char[strlen(cstr)+1];
strcpy(m_data,cstr);
}
else {
m_data = new char[1];
m_data[0] = '\0';
}
}
inline string::~string() {
delete[] m_data;
}
字符串的兩種形式:
- 串+‘\0’
- 長(zhǎng)度字段+串
4. copy ctor & copy op =
淺拷貝 vs. 深拷貝
在copy對(duì)象時(shí)僅copy指針成員屬于淺copy, 創(chuàng)建新的內(nèi)存并將指針的內(nèi)容copy到新內(nèi)存是深copy婴栽。
在class with pointer 默認(rèn)的copy ctor & copy op = 使用的是淺拷貝。
淺拷貝造成的問(wèn)題:
淺拷貝會(huì)造成 alias(別名) 和 memory leak
alias 與 野指針/懸空指針
單一個(gè)內(nèi)存單元被多個(gè)對(duì)象所只向時(shí)辈末,可以看作是該內(nèi)存單元存在了別名愚争。
// p_a 與p_b 構(gòu)成了alias
int *p_a = new int();
int *p_b = p_a;
alias引入的問(wèn)題:
內(nèi)存單元被釋放時(shí),其他地點(diǎn)的別名可能不知道該內(nèi)存以被釋放挤聘,繼而繼續(xù)使用該內(nèi)存轰枝,產(chǎn)生不確定的行為(undefined behavior)。
什么是懸空指針(dangling pointer)?
If a pointer still references the original memory after it has been freed, it is called a dangling pointer.
懸空指針是指針最初指向的內(nèi)存已經(jīng)被釋放了的一種指針组去。
什么是野指針(wild pointer)?
A pointer in c which has not been initialized is known as wild pointer.
野指針(wild pointer)就是沒(méi)有被初始化過(guò)的指針.
5. 拷貝賦值/copy assignment operator
string& string::operator = (const string& str_r) {
if (this == &str_r) { // 自我檢測(cè)
return *this;
}
delete[] m_data;
m_data = new char[strlen(str_r.m_data) + 1];
strcpy(m_data, str_r.m_data);
return *this;
}
注意??:
拷貝賦值中的自我檢測(cè)
copy op=的動(dòng)作:
- 清空已有的數(shù)據(jù)
- 創(chuàng)建新內(nèi)存
- 拷貝
拷貝構(gòu)造與拷貝賦值的差別
- 拷貝構(gòu)造是創(chuàng)建新對(duì)象鞍陨,拷貝已有對(duì)象到新對(duì)象
- 拷貝賦值時(shí),兩個(gè)對(duì)象都已經(jīng)被創(chuàng)建添怔。
基于上述兩點(diǎn)拷貝構(gòu)造無(wú)需檢測(cè)自我賦值湾戳,但拷貝賦值需要贤旷。
內(nèi)存管理
1. stack vs. heap
stack, 是存在于某個(gè)作用域的一塊內(nèi)存空間砾脑。 例如函數(shù)被調(diào)用時(shí)幼驶,函數(shù)本身就會(huì)生成一個(gè)stack。
heap韧衣,是指由操作系統(tǒng)提供的一塊gloabl內(nèi)存空間盅藻,可由程序員動(dòng)態(tài)創(chuàng)建。
2. static object & global object
兩者在生命周期都是在main結(jié)束之后畅铭,程序結(jié)束之后才結(jié)束氏淑。
3. new: 先創(chuàng)建內(nèi)存,后調(diào)用ctor
new的三個(gè)步驟
// 原始語(yǔ)句
complex* pc = new complex(1,2);
// 編譯器內(nèi)部轉(zhuǎn)換后的遇見(jiàn)
void* mem = opreator new(sizeof(complex)); // 1. 分配內(nèi)存
pc = static_cast<complex*>(mem); // 2. 類型轉(zhuǎn)化
pc->complex::complex(1,2); // 3. 調(diào)用構(gòu)造函數(shù)
成員函數(shù)的調(diào)用過(guò)程
哪個(gè)對(duì)象調(diào)用類成員函數(shù)硕噩,(編譯器)默認(rèn)將該對(duì)象的指針傳給被調(diào)用成員函數(shù)this假残。
pc->complex::complex(1,2);
complex::complex(pc,1,2);
3. delete:先調(diào)用dtor, 后釋放內(nèi)存
4. array new 一定要搭配 array delete
擴(kuò)展補(bǔ)充:類模版..
1. C++對(duì)象內(nèi)存模型
- non-static 數(shù)據(jù)成員
- static 數(shù)據(jù)成員
- non-static 成員函數(shù)
- static 成員函數(shù)
- static 數(shù)據(jù)成員/static成員函數(shù)/none -static成員函數(shù) 全局只有一份。
- none-static數(shù)據(jù)成員每個(gè)對(duì)象各有一份
this指針
哪個(gè)對(duì)象調(diào)用成員函數(shù)炉擅,哪個(gè)對(duì)象的指針就會(huì)作為參數(shù)傳遞給成員函數(shù)的this指針
complex c1;
c1.real();
complex::real(&c1);
- none-static 成員函數(shù)通過(guò)this指針將處理不同對(duì)象的non-static數(shù)據(jù)辉懒。
- this pointer如何傳遞:non-static 成員函數(shù)是全局唯一的,哪個(gè)對(duì)象調(diào)用ns成員函數(shù)就將哪個(gè)對(duì)象的地址作為this指針傳遞給ns成員函數(shù)
static 成員
static成員脫離于對(duì)象谍失。成員函數(shù)也是脫離于對(duì)象眶俩。
- static 成員函數(shù)沒(méi)有this pointer
- st 成員函數(shù)只能處理 st數(shù)據(jù)
- st成員函數(shù)沒(méi)有this指針?biāo)圆荒芴幚矸莝t數(shù)據(jù)
靜態(tài)數(shù)據(jù)要在classbody外定義,定義時(shí)不要添加static聲明快鱼。
class acount{
static int a;
}
int acount::a; // 類st成員定義
- st 成員數(shù)據(jù)要在class body 外“定義”颠印。這里是定義,因?yàn)橐峙鋬?nèi)存抹竹。
- static 成員函數(shù)的兩種調(diào)用方式:object調(diào)用线罕,classname 調(diào)用。
設(shè)計(jì)static成員的原則
如果數(shù)據(jù)是在對(duì)象間通用的柒莉,則將該數(shù)據(jù)設(shè)計(jì)為類的static成員闻坚。
static 版本的singleton
class singleton {
public:
static singleton& get_instance() {
static singleton a;
return a;
}
private:
singleton() {};
}
cout
class template
function template
argument deduction (實(shí)參推導(dǎo)):
- argument deduction (實(shí)參推導(dǎo)):不同于class template , function template 在使用時(shí)不用指定具體類型