C++11:using 的各种作用
C++11中using關鍵字的主要作用是:為一個模板庫定義一個別名。
文章鏈接:派生類中使用using別名改變基類成員的訪問權限?
一、《Effective Modern C++》里有比較完整的解釋
各個作用
/*定義別名*/template<class T>using Tlist = std::list<T>;using Tlist = std::list<char>;Tlist listChar;//typedef void (*df)()using df = void(*)();/*使用外部構造*/using A::A;/*引用外部類型*/ using typename A;
二、Using 關鍵字的作用:重載父類函數
1.在當前文件中引入命名空間
???? 這是我們最熟悉的用法,例如:using namespace std;
2.在子類中使用?using?聲明引入基類成員名稱(參見C++ primer)
在private或者protected繼承時,基類成員的訪問級別在派生類中更受限:
class Base { public: std::size_t size() const { return n; } protected: std::size_t n; }; class Derived : private Base { . . . };
在這一繼承層次中,成員函數?size?在?Base?中為?public,但在?Derived?中為?private。為了使?size?在?Derived?中成為?public,可以在?Derived?的?public
部分增加一個?using?聲明。如下這樣改變?Derived?的定義,可以使?size?成員能夠被用戶訪問,并使?n?能夠被?Derived的派生類訪問:
class Derived : private Base { public: using Base::size; protected: using Base::n; // ... };
另外,當子類中的成員函數和基類同名時,子類中重定義的成員函數將隱藏基類中的版本,即使函數原型不同也是如此(隱藏條件見下面)。
如果基類中成員函數有多個重載版本,派生類可以重定義所繼承的?0?個或多個版本,但是通過派生類型只能訪問派生類中重定義的那些版本,所以如果派生類想通過自身類型使用所有的重載版本,則派生類必須要么重定義所有重載版本,要么一個也不重定義。有時類需要僅僅重定義一個重載集中某些版本的行為,并且想要繼承其他版本的含義,在這種情況下,為了重定義需要特化的某個版本而不得不重定義每一個基類版本,可能會令人厭煩。可以在派生類中為重載成員名稱提供 using 聲明(為基類成員函數名稱而作的 using 聲明將該函數的所有重載實例加到派生類的作用域),使派生類不用重定義所繼承的每一個基類版本。一個 using 聲明只能指定一個名字,不能指定形參表,使用using聲明將名字加入作用域之后,派生類只需要重定義本類型確實必須定義的那些函數,對其他版本可以使用繼承的定義。
“隱藏”是指派生類的函數屏蔽了與其同名的基類函數,規則如下:
1、如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)
2、如果派生類的函數與基類的函數同名,并且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)
#include "StdAfx.h" #include <iostream> using namespace std; class Base { public: void menfcn(){cout<<"Base function"<<endl; }void menfcn(int n){cout<< cout<<"Base function with int"<<endl; } };class Derived : Base { public: using Base::menfcn;//using聲明只能指定一個名字,不能帶形參表 int menfcn(int) { cout<< cout<<"Derived function with int"<<endl; } }; int main() { Base b; Derived d; b.menfcn(); d.menfcn();//如果去掉Derived類中的using聲明,會出現錯誤:error C2660: 'Derived::menfcn' : function does not take 0 arguments std::cin.ignore(std::cin.gcount()+1);//清空緩沖區 std::cin.get();//暫停程序執行 }
三、需要注意的情況
子類中using引入基類函數時需要注意的情況class base{ public:void test(){cout << "base::test()" << endl;}void test(int){cout << "base::test(int)" << endl;} }; class derived : public base{ public:void test(){cout << "derived::test()" << endl;} };
此時derived::test()會隱藏(hide)父類中的兩個test重載函數(base::test()和base::test(int)),因此我們為子類中加上一個using聲明:
class derived : public base{ public:void test(){cout << "derived::test()" << endl;}using base::test;//此聲明放在test前面和后面效果都一樣 };
現在會不會出現下面所述的情況呢?
---------------------------------------------------------------------------------------------------------------
既然using base::test將父類中的兩個test函數都引入子類,則子類中就相當于有了一個void test()函數,所以我們在子類中重新定義的void test()函數將會和從父類中引入的void test()函數發生沖突,進而出現“重定義”錯誤。
---------------------------------------------------------------------------------------------------------------
答案是:不會!
此時,子類中重新定義的void test()函數將“頂替”從父類中引入的void test()函數。
(PS:從父類中引入的另外一個void test(int)函數則沒有發生變化(仍然是父類中的函數實現)。) 類似的另外一種情況如下,此時加入了virtual:
class base{ public:virtual void test(){cout << "base::test()" << endl;}virtual void test(double){cout << "base::test(double)" << endl;}void test(int){cout << "base::test(int)" << endl;} }; class derived : public base{ public:void test(){cout << "derived::test()" << endl;} };
此時derived::test()雖然重寫(override)了base::test(),但是同時也隱藏(hide)父類中的兩個test重載函數(一個virtual函數base::test(double)和一個nonvirtual函數base::test(int))。現在,我們為子類中加上一個using聲明:
class derived : public base{ public:void test(){cout << "derived::test()" << endl;}using base::test;//此聲明放在test前面和后面效果都一樣 };
與上面的類似,此時derived::test()“仍然重寫”了父類的base::test(),并且與父類中的base::test(double)和base::test(int)[在子類的域]中形成重載集合。
最后,留一個思考題目,如下:
class base{ public:virtual void test(){cout << "base::test()" << endl;}virtual void test(double){cout << "base::test(double)" << endl;}void test(int){cout << "base::test(int)" << endl;} }; class derived : public base{ public:void test(){cout << "derived::test()" << endl;}//using base::test; }; class A : public derived{ public:void test(double){cout << "A::test(double)" << endl;} }; int main(int argc, char **argv){base *pb = new A;pb->test(2.4);return 0; }
問題:derived中的using base::test加上與否,對程序的結果有什么影響?
答:沒有影響。(關鍵點:名字解析是編譯時期的事情,而virtual函數動態綁定是運行時期的事情。)
(PS:但是將main函數改成“derived *pd = new A; pd->test(2.4);”,則有區別了:如果將using base::test去掉,則編譯失敗。)
總結
以上是生活随笔為你收集整理的C++11:using 的各种作用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Detectron:Pytorch-Ca
- 下一篇: 鲨鱼记账是什么平台