C++進階學習
標頭檔案的防衛式宣告
#ifndef MYCLASS
#define MYCLASS
Header file content
#endif
成員函式的保護
- 如果輸入引數採用“值傳遞”,由於函式將自動產生臨時變數用於複製該引數,該輸入引數本來就無需保護,所以不要加 const 修飾。例如不要將函式
void Func1(int x)
寫成void Func1(const int x)
。 - 如果引數作為輸出引數,不論它是什麼資料型別,也不論它採用“指標傳遞”還是“引用傳遞”,都不能加 const 修飾,否則該引數將失去輸出功能(因為有 const 修飾之後,不能改變他的值)。
- 如果引數作為輸入引數,可以防止資料被改變,起到保護作用,增加程式的健壯性,建議是能加const儘量加上
int getToValue() const //此處需要加入const,防止shu'j
{
return m_nTo; //this->m_nTo
}
返回值最佳化
return by reference 的情況包括一個value加到另一個value的情況,不適用於兩個value相互作用產生新的value的情況,正如黃色內的內容
return by reference 不需要知道接收者是以reference形式接收的
特殊的過載運算子
<< 不能寫為成員函式,只能寫全域性函式,因為cout不能識別複數型別
inline complex
conj (const complex& x){
return complex(real(x) , -imag(x));
}
#include <iostream>
ostream& //防止第二種情況的發生,需要“cout<<c1”的型別為cout才能接收第二個conj
operator << (ostream& os , const complex& x){ //ostream沒有const因為
//向cout內傳入資料就是在改變cout
return os << '(' <<real(x)<<','<<imag(x)<<')';
}
int main()
{
complex c1(2 , 1);
cout<<conj(c1);
cout<<c1<<conj(c1); //②
}
Inline函式
Inline函式的使用
如果是在類中,函式的如果在類定義的時候進行定義,那麼,加不加關鍵字 inline 並沒有什麼異樣,因為,這樣的函式都會被預設為是行內函數。
詳細:C++經驗(十一)-- (inlining)行內函數的使用_slowlytalk的部落格-CSDN部落格
三種特殊的函式
//建構函式
inline
String::String(const char* cstr = 0){//引數為常量指標
if(cstr){
m_data = new char[strlen(cstr) + 1];//"+1"的原因是字串最後還有一個 結束位
strcpy(m_data , cstr);
}
else{
m_data = new char[1];
*m_data = '\0';
}
}
//解構函式
inline
String::String(){
delete[] m_data;
}
//複製建構函式
inline
String::String(const String& str){
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data , str.m_data);
}
//複製賦值函式
inline
String& String::String operator=(const String& str){
if(this == &str){
return *this; //監測自我賦值,
}
delete[] m_data;
m_data = new char[strlen(strlen m_data) + 1];
strcpy(m_data , str.m_data);
return *this;
}
記憶體管理
若使用array new 則一定要使用array delete
String *p = new String[3];
...
delete[] p; //因為delete的陣列所以要有[],否則無法徹底清除
堆和棧
class Complex {....}
...
{
Complex c1(1,2);//c1所佔用的空間來自於Stack
Complex* p = new Complex(3);//p是個臨時物件,空間是從Heap的分配而來的
}
Stack的生命期結束之後清理,也是auto object
static Stack的生命在作用域結束之後仍然存在,在函式結束後才會結束
global object 在類結束後再消失
delete:先呼叫dtor(解構函式)後釋放memory
new:先分配memory 後呼叫ctor(建構函式)
static
一般常量型別(未被static
修飾)、引用型別,在類內部即可實現初始化,在類外實現初始化的必定是static
型別(當然一部分的靜態成員,也即同時又是const和integral type的,可在類內初始值設定,這一語法特性並非為所有的編譯器所支援,所以一種推薦的做法,即是凡是static型別的,我們總在類外進行初始化,哪怕它在類內已進行初始值設定,只要在類外初始化時不修改其值):
int Test::si = 0;
const int Test::sci = 1;
const double Test::scd = 99.0;
函式模板
遇到不能立刻確定的資料和函式型別,可以用function template 來暫時定義。
template <class T>
inline
const T& min(const T& a , const T& b){
return b < a ? b : a;}
Composition 表示has-a
template <class T>
class queue{
protected:
deque<T> c;//引用了別的類的資料
};
class deque{
protected:
Itr<T> star;// 同上
Ttr<T> finish;
T** map;
unsigned int map_size;
};
struct Ttr{
T* cur;
T* first;
T* last;
T** node;
};
組合下的構造和析構:
構造順序為由內而外,析構為由外而內
Delegation Composition by reference
vector 的一些基礎操作
向尾部加入元素時由兩種函式,一個是push_back(),另一個是emplace_back()
- push_back 可以接收左值也可以接受右值,接收左值時使用複製構造,接收右值時使用移動構造
- emplace_back 接收右值時呼叫類的移動構造
- emplace_back 接收左值時,實際上的執行效果是先對傳入的引數進行複製構造,然後使用複製構造後的副本,也就是說,emplace_back在接收一個左值的時候其效果和push_back一致!所以在使用emplace_back 時需要確保傳入的引數是一個右值引用,如果不是,請使用std::move()進行轉換
一些庫函式的練習
String
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;}//用於cout的函式
private:
char* m_data;
}
//建構函式
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';
}
}
//解構函式
inline
String::~String()
{
delete[] m_data; //使用array new所以要用delete array
}
//複製建構函式
inline
String::String(const String& str)
{
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data , str.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;
}
STL
體驗STL庫中模板的組成,一個模板可能是由好幾個模板相互疊加而形成的
#include <vector>
#include <algorithm>
#inlcude <functional>
#inlcude <iostream>
using namespace std;
int main(){
int ia[6] = {27 , 210 , 12 , 47 , 109 , 83};
vector<int , allocator<int>> vi(ia , ia + 6);//第二個引數可以不寫,因為預設分配
cout<<count_if(vi.begin() , vi.end() , not1(bind2nd(less<int>() , 40)));
return 0;
}
STL容器
#include <vector>
#include <stdexcept>
#include <string>
#include <cstdlib> //abort()
#include <cstdio> //snprintf()
#include <iostream>
#include <ctime>
#include <algorithm> //sort()
namespace jj02
{
void test_vector(long& value)
{
cout << "\ntest_vector().......... \n";
vector<string> c;
char buf[10];
clock_t timeStart = clock();
for(long i=0; i< value; ++i)
{
try {
snprintf(buf, 10, "%d", rand());
c.push_back(string(buf));
}
catch(exception& p) {
cout << "i=" << i << " " << p.what() << endl;
//曾經最高 i=58389486 then std::bad_alloc
abort();//捕捉到錯誤後及時退出程式
}
}
cout << "milli-seconds : " << (clock()-timeStart) << endl;
cout << "vector.max_size()= " << c.max_size() << endl; //1073747823
cout << "vector.size()= " << c.size() << endl;
cout << "vector.front()= " << c.front() << endl;
cout << "vector.back()= " << c.back() << endl;
cout << "vector.data()= " << c.data() << endl;
cout << "vector.capacity()= " << c.capacity() << endl << endl;
string target = get_a_target_string();
{
timeStart = clock();//記錄第一次時間戳
auto pItem = find(c.begin(), c.end(), target);//返回型別為iterator
cout << "std::find(), milli-seconds : " << (clock()-timeStart) << endl;
//記錄事件差
if (pItem != c.end())
cout << "found, " << *pItem << endl << endl;
else
cout << "not found! " << endl << endl;
}
{
timeStart = clock();
sort(c.begin(), c.end());
cout << "sort(), milli-seconds : " << (clock()-timeStart) << endl;
timeStart = clock();
string* pItem = (string*)::bsearch(&target, (c.data()),
c.size(), sizeof(string), compareStrings);
cout << "bsearch(), milli-seconds : " << (clock()-timeStart) << endl;
if (pItem != NULL)
cout << "found, " << *pItem << endl << endl;
else
cout << "not found! " << endl << endl;
}
c.clear();
test_moveable(vector<MyString>(),vector<MyStrNoMove>(), value);
}
}
#include <list>
#include <stdexcept>
#include <string>
#include <cstdlib> //abort()
#include <cstdio> //snprintf()
#include <algorithm> //find()
#include <iostream>
#include <ctime>
namespace jj03
{
void test_list(long& value)
{
cout << "\ntest_list().......... \n";
list<string> c;
char buf[10];
clock_t timeStart = clock();
for(long i=0; i< value; ++i)
{
try {
snprintf(buf, 10, "%d", rand());
c.push_back(string(buf));
}
catch(exception& p) {
cout << "i=" << i << " " << p.what() << endl;
abort();
}
}
cout << "milli-seconds : " << (clock()-timeStart) << endl;
cout << "list.size()= " << c.size() << endl;
cout << "list.max_size()= " << c.max_size() << endl; //357913941
cout << "list.front()= " << c.front() << endl;
cout << "list.back()= " << c.back() << endl;
string target = get_a_target_string();
timeStart = clock();
auto pItem = find(c.begin(), c.end(), target);
cout << "std::find(), milli-seconds : " << (clock()-timeStart) << endl;
if (pItem != c.end())
cout << "found, " << *pItem << endl;
else
cout << "not found! " << endl;
timeStart = clock();
c.sort();
cout << "c.sort(), milli-seconds : " << (clock()-timeStart) << endl;
c.clear();
test_moveable(list<MyString>(),list<MyStrNoMove>(), value);
}
}