C++11:using 的各類做用

C++11中using關鍵字的主要做用是:爲一個模板庫定義一個別名。html

文章連接:派生類中使用using別名改變基類成員的訪問權限 
ios

1、《Effective Modern C++》裏有比較完整的解釋ide

各個做用函數

/*定義別名*/
 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;

2、Using 關鍵字的做用:重載父類函數
spa

1.在當前文件中引入命名空間.net

     這是咱們最熟悉的用法,例如:using namespace std;htm

2.在子類中使用 using 聲明引入基類成員名稱(參見C++ primer)blog

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的派生類訪問:ci

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();//暫停程序執行  
}

3、須要注意的狀況

子類中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去掉,則編譯失敗。)
相關文章
相關標籤/搜索