2010-01-08 67 views
2

我一直在嘗試學習更多關於私有繼承的知識,並決定創建一個string_t類,它繼承自std::basic_string。我知道很多你會告訴我從STL類繼承是一個壞主意,如果我想擴展它們的功能,最好創建接受對這些類的實例的引用的全局函數。我同意,但正如我之前所說,我正試圖學習如何實現私有繼承。來自std :: basic_string的私有繼承

這是類的樣子至今:

class string_t : 
#if defined(UNICODE) || defined(_UNICODE) 
    private std::basic_string<wchar_t> 
#else 
    private std::basic_string<char> 
#endif 
{ 
public: 
    string_t() : basic_string<value_type>() {} 

    string_t(const basic_string<value_type>& str) 
    : basic_string<value_type>(str) {} 

    virtual ~string_t() {} 

    using std::basic_string<value_type>::operator=; /* Line causing error */ 

    std::vector<string_t> split(const string_t& delims) 
    { 
    std::vector<string_t> tokens; 

    tokens.push_back(substr(0, npos)); 
    } 
}; 

我收到以下錯誤:

 
1>c:\program files\microsoft visual studio 9.0\vc\include\xutility(3133) : error C2243: 'type cast' : conversion from 'const string_t *' to 'const std::basic_string &' exists, but is inaccessible 
1>  with 
1>  [ 
1>   _Elem=wchar_t, 
1>   _Traits=std::char_traits, 
1>   _Ax=std::allocator 
1>  ] 

1>  c:\program files\microsoft visual studio 9.0\vc\include\xutility(3161) : see reference to function template instantiation 'void std::_Fill(_FwdIt,_FwdIt,const _Ty &)' being compiled 
1>  with 
1>  [ 
1>   _Ty=string_t, 
1>   _FwdIt=string_t * 
1>  ] 

1>  c:\program files\microsoft visual studio 9.0\vc\include\vector(1229) : see reference to function template instantiation 'void std::fill(_FwdIt,_FwdIt,const _Ty &)' being compiled 
1>  with 
1>  [ 
1>   _Ty=string_t, 
1>   _FwdIt=string_t * 
1>  ] 

1>  c:\program files\microsoft visual studio 9.0\vc\include\vector(1158) : while compiling class template member function 'void std::vector::_Insert_n(std::_Vector_const_iterator,unsigned int,const _Ty &)' 
1>  with 
1>  [ 
1>   _Ty=string_t, 
1>   _Alloc=std::allocator 
1>  ] 

1>  c:\work\c++\string_t\string_t.h(658) : see reference to class template instantiation 'std::vector' being compiled 
1>  with 
1>  [ 
1>   _Ty=string_t 
1>  ] 

中的最後一個錯誤點的行號(658)來的左括號split()函數定義。如果我註釋掉using std::basic_string<value_type>::operator=;這一行,我可以擺脫這個錯誤。據我所知,using關鍵字指定賦值運算符從privatepublic範圍內的string_t

爲什麼我得到這個錯誤,我該如何解決?

此外,我的string_t類不包含它自己的單個數據成員,更不用說任何動態分配的成員。所以如果我不爲這個類創建一個析構函數並不意味着如果有人使用基類指針的一個實例,基類的析構函數會被調用?

下面的代碼在我爲string_t定義析構函數時會拋出異常,但在使用VS2008編譯時註釋掉析構函數時會發生異常。

basic_string<wchar_t> *p = new string_t(L"Test"); 
delete p; 
+3

你不覺得你會用它的東西,你*不*都不理大家的意見更多地瞭解私有繼承? ;) – jalf 2010-01-08 12:23:23

+0

私有繼承通常是一個壞主意。使用它的唯一理由是應用EBO優化(空基優化),並且由於'std :: string'有數據,所以它不具備資格。所有其他「原因」(如虛擬重定義)都有其他解決方案(在這種情況下,公共繼承+組合)。繼承是一個非常強大的關係,特別是在知名度方面,它比友誼好,但不是太多。對於這個問題,你可以完美地添加自由函數來擴展接口。 – 2010-01-08 17:46:02

+1

@Matthieu - 在這種情況下公共繼承的問題是std :: basic_string沒有虛擬析構函數;因此試圖使用基類指針刪除string_t的實例會導致內存泄漏。也許你可以回答第二個問題:如果string_t類不需要析構函數而不聲明它,那麼我提到的使用delete的問題是否仍然存在? – Praetorian 2010-01-08 18:23:31

回答

3

您的默認構造函數不應該是顯式的。我認爲明確可能是它不能將std::string轉換爲string_t的原因,但是您從您的片段中刪除了該建築師:vP。

這個程序編譯和運行良好與GCC 4.2:

#include <iostream> 
#include <string> 
#include <vector> 
using namespace std; 

class string_t : 
#if defined(UNICODE) || defined(_UNICODE) 
    private std::basic_string<wchar_t> 
#else 
    private std::basic_string<char> 
#endif 
{ 
public: 
    string_t() : basic_string<value_type>() {} 

    string_t(const basic_string<value_type>& str) 
    : basic_string<value_type>(str) {} 

    virtual ~string_t() {} 

    using std::basic_string<value_type>::operator=; /* Line causing error */ 

    std::vector<string_t> split(const string_t& delims) 
    { 
    std::vector<string_t> tokens; 

    for (size_t pen = 0, next = 0; next != npos; pen = next + 1) { 
     next = find_first_of(delims, pen); 
     if (pen != next) tokens.push_back(substr(pen, next - pen)); 
    } 
    return tokens; 
    } 

    template<class os> 
    friend os &operator<<(os &, string_t const&); 
}; 

template< class os_t > 
os_t &operator<<(os_t &os, string_t const &str) { 
     return os << static_cast<string>(str); 
} 

int main(int argc, char ** argv) { 
     vector<string_t> mytoks = string_t(argv[1]).split(string("_")); 

     for (vector<string_t>::iterator it = mytoks.begin(); it != mytoks.end(); ++ it) { 
       cerr << * it << endl; 
     } 
     return 0; 
} 
+0

我複製粘貼你的代碼並嘗試編譯它,但是VS2008給了我同樣的錯誤! – Praetorian 2010-01-08 21:44:30

2

您需要提供合適的轉化構造函數:

string_t(const std::basic_string<value_type>&); 

否則編譯器不知道如何從std::basic_string<>構造string_t當你添加元素的string_t S上的矢量。

關於更新:
A usingoperator=()聲明不會幫助,如果它是私人的。爲什麼不只是實現自己的operator=(),而不是和轉發任務:

string_t& operator=(const string_t& s) 
{ 
    basic_string<value_type>::operator=(s); 
    return *this; 
} 

有了它建立對我很好。

+0

我已經有一個構造函數定義如下: string_t(常量的std :: basic_string的&):basic_string的(){} (很抱歉,但我是比較新的模板,所以我可能會丟失一些東西明顯) – Praetorian 2010-01-08 06:13:00

+0

爲什麼不給我們一個小的可編譯的例子,然後展示問題呢?添加轉換構造函數使得代碼可以爲我編譯,並且不會顯示您提到的第一個問題。 – 2010-01-08 07:09:56

+0

轉換不應該是私人的。他遇到了問題,因爲「轉換存在,但無法訪問」; v) – Potatoswatter 2010-01-08 08:46:47

0

不幸的是,你已經遺漏了足夠的數據,不清楚你是如何得到你已經注意到的錯誤信息的(並且這無助於我們在編譯它時不知道657行是什麼。 )。

目前可能似乎是substr()返回一個std :: string,編譯器不知道如何將其轉換爲string_t(這是你要求它存儲在向量中)。你可能需要/想添加一個構造函數來處理這個問題,是這樣的:

string_t(std::string const &init) : std::string(init) {} 

這讓編譯器知道如何通過substr()返回的字符串轉換成string_t按照您的要求,它可以推到載體。

+0

對不起,關於行號,第657行是定義split()函數的行,它是包含精確的左括號的行。 我有一個轉換構造函數定義如下: string_t(常量的std :: basic_string的&):basic_string的(){} 難道不應該是一樣的,你已經說了什麼? – Praetorian 2010-01-08 06:31:40

+0

不,它接受'std :: basic_string '作爲參數,但未能將參數傳遞給基類。 – 2010-01-08 14:51:02

+0

對不起,這只是一個錯字。構造函數實際上定義爲:string_t(const basic_string &str) :basic_string (str){} – Praetorian 2010-01-08 17:26:04