輸入輸出
輸入
- cin >>
跳過空白字符(空格、換行符、製表符)。
#include <iostream>
#include <string>
int main()
{
using std::cout;
using std::cin;
using std::endl;
using std::string;
string tmp("");
cout << "input:";
cin >> tmp;
cout << "output:" << tmp <<endl;
return 0;
}
輸出結果:
- cin.get()
該函數的聲明有5種,如下。
int_type get();
_Myt& get(_Elem *_Str, streamsize _Count);
_Myt& get(_Elem *_Str, streamsize _Count, _Elem _Delim);
_Myt& get(_Elem& _Ch);
_Myt& get(_Mysb& _Strbuf);
_Myt& get(_Mysb& _Strbuf, _Elem _Delim);
輸入流中取字符或者字符串。不跳過空白字符。如果是字符串,那麼 最多隻能輸入_Count-1個字符,然後最後補上一個字符串的結束符。_Delim是結束符。get函數停止接收字符的條件有2個,遇到文件結尾EOF或者結束符,或者已經取到_Count-1個字符。哪個條件新到達,取字符結束。
- cin.getline()
友元
類的機制實現了數據的隱藏與封裝。類的數據成員一般定義爲私有成員,成員函數一般定義爲公有成員。提供類與外界間的通信接口。但有時需要定義一些函數,這些函數不是類的一部分,但又需頻繁訪問類的數據成員,這時可以將這些函數定義爲該函數的友元函數。除了友元函數外,還有友元類,兩者統稱爲友元。友元的作用是提高了程序的運行效率(即減少了類型檢查和安全性檢查等都需要時間開銷),但它也破壞了類的封裝性和隱藏性,使得非成員函數可以訪問類的私有成員。
- 友元函數
友元函數 :可以直接訪問類的私有成員的非成員函數。它是定義在類外的普通函數,它不屬於任何類,但需要在類的定義中加以聲明,聲明時只需在友元的名稱前加上關鍵字friend,其格式如下:
friend 函數返回類型 函數名(形式參數);
注意:1.友元函數的聲明可以放在類的私有部分,也可以放在公有部分,沒有區別的,都說明是該類的一個友元函數
2. 一個函數可以是多個類的友元函數,只需要在各個類中分別聲明
3. 友元函數的調用與一般函數的調用方式和原理一致
- 友元類
友元類 :友元類的所有成員函數都是另一個類的友元函數,都可以訪問另一個類中的隱藏信息(包括私有成員和保護成員)。當希望一個類可以存取另一個類的私有成員時,可以將該類聲明爲另一類的友元類。定義友元類的語句格式如下:
friend class 類名; //friend和class是關鍵字,類名必須是程序中的一個已定義過的類。
例:
class B;
class A
{
…
public:
friend class B;
…
};
class C : public A{}; // C直接繼承A,但是無法實例化,友元關係不能傳遞
class B : public A{}; // B直接繼承A,可以實例化
class D : public B{}; // D直接繼承B,可以實列化,相當於D間接繼承A
類B的所有成員函數都是類A的友元函數,能存取類A的私有成員和保護成員。使用友元類時注意:1.友元關係不能被繼承
2. 元關係是單向的,不具有交換性。若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應的聲明
3. 友元關係不具有傳遞性。若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應的申明
友元類特點:(上例)將導致A無法被除B以外的其它任何類直接繼承以後實例化。可事實是,下面這段代碼可以正常編譯與運行。
#include <iostream>
using namespace std;
class A
{
public:
A(){_a = 100; _b = 200;}
A(int x, int y) : _a(x), _b(y) {}
void print_pub_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
private:
int _a, _b;
void print_priv_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
friend class B;
};
class B : public A
{
public:
B() : _c(9988) {}
void print_pub_b()
{
cout << "class B print function." << endl;
a1.print_priv_a();
}
private:
int _c;
A a1;
};
class C : public A
{
public:
void print_pub_c()
{
cout << "public print function in CLASS C." << endl;
}
};
int main(void)
{
C c1;
c1.print_pub_a();
}
把上面的代碼稍作改動(把Class A的構造函數全部放入private域) ,如下,編譯失敗。
#include <iostream>
using namespace std;
class A
{
public:
void print_pub_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
private:
A(){_a = 100; _b = 200;}
A(int x, int y) : _a(x), _b(y) {}
int _a, _b;
void print_priv_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
friend class B;
};
class B : public A
{
public:
B() : _c(9988) {}
void print_pub_b()
{
cout << "class B print function." << endl;
a1.print_priv_a();
}
private:
int _c;
A a1;
};
class C : public A
{
public:
void print_pub_c()
{
cout << "public print function in CLASS C." << endl;
}
};
int main(void)
{
C c1;
c1.print_pub_a();
}
再對上面的代碼稍作改動(main函數中代碼全部註釋掉) ,編譯成功。
#include <iostream>
using namespace std;
class A
{
public:
void print_pub_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
private:
A(){_a = 100; _b = 200;}
A(int x, int y) : _a(x), _b(y) {}
int _a, _b;
void print_priv_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
friend class B;
};
class B : public A
{
public:
B() : _c(9988) {}
void print_pub_b()
{
cout << "class B print function." << endl;
a1.print_priv_a();
}
private:
int _c;
A a1;
};
class C : public A
{
public:
void print_pub_c()
{
cout << "public print function in CLASS C." << endl;
}
};
int main(void)
{
//C c1;
//c1.print_pub_a();
}
從上面的例子可以知道,class C的實例化必須要調用Class的構造函數,如果構造函數在private域,那麼自然不能實例化,但是可以繼承。
class C是直接從A繼承,如果 讓class c從B繼承又會怎麼樣呢?看下面的代碼,C從B處間接繼承A,因爲B是A的友元函數,所以即使A的構造函數在private域,那麼C也是可以調用到,因此可實例化。
#include <iostream>
using namespace std;
class A
{
public:
void print_pub_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
private:
A(){_a = 100; _b = 200;}
A(int x, int y) : _a(x), _b(y) {}
int _a, _b;
void print_priv_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
friend class B;
};
class B : public A
{
public:
B() : _c(9988) {}
void print_pub_b()
{
cout << "class B print function." << endl;
a1.print_priv_a();
}
private:
int _c;
A a1;
};
class C : public B
{
public:
void print_pub_c()
{
cout << "public print function in CLASS C." << endl;
}
};
int main(void)
{
C c1;
c1.print_pub_a();
}
在上面的代碼中,如果不讓C通過B的友元間接繼承A,那麼可以像下面這樣實現。A的構造函數放在private域,B是A的友元類,同時B從A共有繼承(使用virtual)。那麼沒有其他的類可以直接或者間接的從A處繼承。下面編譯會失敗。
#include <iostream>
using namespace std;
class A
{
public:
void print_pub_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
private:
A(){_a = 100; _b = 200;}
A(int x, int y) : _a(x), _b(y) {}
int _a, _b;
void print_priv_a()
{
cout << "mem1 =" << _a;
cout << "mem2_=" << _b;
cout << endl;
}
friend class B;
};
class B : virtual public A
{
public:
B() : _c(9988) {}
void print_pub_b()
{
cout << "class B print function." << endl;
a1.print_priv_a();
}
private:
int _c;
A a1;
};
class C : public B
{
public:
void print_pub_c()
{
cout << "public print function in CLASS C." << endl;
}
};
int main(void)
{
C c1;
c1.print_pub_a();
}
在上面的代碼中,實例化c1時,C直接調用A的構造函數,而不是間接調用B的構造,所以會實例化失敗。
1. 將A的構造函數放入private區域。2. 聲明子類B爲A的友元。3. 定義A爲virtual public繼承B。
C++關鍵字
-
final 指定符 (C++11 起)
在C++11中關鍵字final,用來指定派生類不能覆寫虛函數,或類不能被繼承。應用到成員函數時,標識符final 在類定義中的成員函數聲明或成員函數定義的語法中,立即出現於聲明器後。應用到類時,標識符 final
出現在類定義的起始,立即在類名後。例如下面leveldb代碼中,類PosixLogger不能被其他的類繼承了
namespace leveldb {
class PosixLogger final : public Logger {
public:
// Creates a logger that writes to the given file.
//
// The PosixLogger instance takes ownership of the file handle.
explicit PosixLogger(std::FILE* fp) : fp_(fp) {
assert(fp != nullptr);
}
~PosixLogger() override {
std::fclose(fp_);
}
.......
.......
.......
private:
std::FILE* const fp_;
};
} // namespace leveldb
#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
-
override 指定符(C++11 起)
在C++11中關鍵字override,它用來指定基類中的虛成員函數需要在子類中顯式地重寫,如果沒有重寫則編譯器報錯。例如在上面代碼中就有關鍵字override的使用
-
default 指定符
在C++11中關鍵字default,它用來顯式默認化的函數定義,令編譯器爲類生成特殊成員函數或比較運算符 (C++20 起)的顯式指令。其中特殊成員函數有:默認構造函數、複製構造函數、移動構造函數 (C++11 起)、複製賦值運算符、移動賦值運算符 (C++11 起)、析構函數。即,如果某個特殊成員函數在聲明時使用了default,那麼編譯器會爲該函數自動生成函數體。例如leveldb代碼
namespace leveldb
{
class LEVELDB_EXPORT Cache;
// Create a new cache with a fixed size capacity. This implementation
// of Cache uses a least-recently-used eviction policy.
LEVELDB_EXPORT Cache* NewLRUCache(size_t capacity);
class LEVELDB_EXPORT Cache {
public:
Cache() = default;
Cache(const Cache&) = delete;
Cache& operator=(const Cache&) = delete;
// Destroys all existing entries by calling the "deleter"
// function that was passed to the constructor.
virtual ~Cache();
// Opaque handle to an entry stored in the cache.
struct Handle { };
......
......
}//class Cache
}//namespace leveldb
關於cache的定義時,使用了該關鍵字。
-
delete 指定符
在C++11中關鍵字delete,它的定義與default是一樣的,但是功能不一樣。delete指示禁止使用特殊成員函數。在上面的代碼中使用到了關鍵字delete
- static
在類中聲明static變量或者函數時,初始化時使用作用域運算符來標明它所屬類,因此,靜態數據成員是類的成員,而不是對象的成員,這樣就出現以下作用:
(1)類的靜態成員函數是屬於整個類而非類的對象,所以它沒有this指針,這就導致它僅能訪問類的靜態數據和靜態成員函數。
(2)不能將靜態成員函數定義爲虛函數。
(3)由於靜態成員聲明於類中,操作於其外,所以對其取地址操作,就多少有些特殊,變量地址是指向其數據類型的指針,函數地址類型是一個“nonmember函數指針”。
(4)由於靜態成員函數沒有this指針,所以就差不多等同於nonmember函數,結果就產生了一個意想不到的好處:成爲一個callback函數,使得我們得以將C++和C-based X W indow系統結合,同時也成功的應用於線程函數身上。(這條沒遇見過)
(5)static並沒有增加程序的時空開銷,相反她還縮短了子類對父類靜態成員的訪問時間,節省了子類的內存空間。
(6)靜態數據成員在<定義或說明>時前面加關鍵字static。
(7)靜態數據成員是靜態存儲的,所以必須對它進行初始化。(程序員手動初始化,否則編譯時一般不會報錯,但是在Link時會報錯誤)
(8)靜態成員初始化與一般數據成員初始化不同:
初始化在類體外進行,而前面不加static,以免與一般靜態變量或對象相混淆;
初始化時不加該成員的訪問權限控制符private,public等;
初始化時使用作用域運算符來標明它所屬類;
所以我們得出靜態數據成員初始化的格式:<數據類型><類名>::<靜態數據成員名>=<值>
(9)爲了防止父類的影響,可以在子類定義一個與父類相同的靜態變量,以屏蔽父類的影響。這裏有一點需要注意:我們說靜態成員爲父類和子類共享,但我們有重複定義了靜態成員,這會不會引起錯誤呢?不會,編譯器採用了一種絕妙的手法:name-mangling 用以生成唯一的標誌。
#include <iostream>
using namespace std;
class UnitTest
{
public:
UnitTest* GetInstance(void)
{
static UnitTest instance;
return &instance;
}
};
int main(void)
{
UnitTest u;
cout << u.GetInstance() << endl;
cout << u.GetInstance() << endl;
}
上面 ,在函數GetInstance(void)中,不論調用多少次,instance只定義一次(第一次被調用時),後面每一次調用時得到相同的instance。
- const
const修飾成員函數,不能修改任何的成員變量(mutable修飾的變量除外)
const成員函數不能調用非const成員函數,因爲非const成員函數可以會修改成員變量
//google test source code
const TestInfo* current_test_info() const GTEST_LOCK_EXCLUDED_(mutex_);
- explicit
explicit可以抑制內置類型隱式轉換,所以在類的構造函數中,最好儘可能多用explicit關鍵字,防止不必要的隱式轉換.