2013-03-08 63 views
1

這是從Instance-level encapsulation with C++後續的帖子。私人類成員沒有完全封裝?

我已經定義了一個類,並從該類創建了兩個對象。

#include <iostream> 
#include <ctime> 
#include <string> 

using namespace std; 

class timeclass { 
    private: 
    string date; 

    time_t gmrawtime, rawtime; 
    struct tm * timeinfo; 
    char file_date[9]; 

    void tm_init(int); 

public: 
    timeclass(int); 
    void print_date(); 
}; 

void timeclass::tm_init(int y) { 
    timeinfo = gmtime(&rawtime); 
    timeinfo->tm_year = y - 1900; // timeinfo->tm_year holds number of years since 1900 
    timeinfo->tm_mon = 0; 
    timeinfo->tm_mday = 1; 
    timeinfo->tm_hour = 0; 
    timeinfo->tm_min= 0; 
    timeinfo->tm_sec= 0; 
} 

timeclass::timeclass(int y) { 
    timeclass::tm_init(y); 
    gmrawtime = mktime(timeinfo) - timezone; 
} 

void timeclass::print_date() { 
    strftime(file_date,9,"%Y%m%d",timeinfo); 

    date = string(file_date); 
    cout<<date<<endl; 
} 

/* -----------------------------------------------------------------------*/ 

int main() 
{ 
    timeclass time1(1991); 
    timeclass time2(1992); 

    time1.print_date(); // Prints 19920101, despite being initialized with 1991 
    time2.print_date(); // Prints 19920101, as expected 

    return 0; 
} 

這個例子是從我的主程序切片和切塊的日期計數器的一部分,但它說明了我的觀點。我想爲每個類的實例運行一個日期計數器(time1和time2),但是看起來像一旦我構造了time2對象,我認爲在time1中封裝的'timeinfo'變量被time2構造函數覆蓋。

我知道C++僅支持類級封裝,並且想知道我的問題是否因爲同一類的成員可以訪問彼此的私有成員。有沒有辦法解決這個問題,所以我可以實現我想要做的事情?謝謝你, 泰勒

+1

避免做'使用命名空間std;'。請參閱[這裏](http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-a-bad-practice-in-c)的解釋。 – AxelOmega 2013-03-08 21:21:30

+0

謝謝@AxelOmega,我歡迎任何提示,因爲我不是這方面的專家。你會建議省略'使用namespace std;',然後直接調用std :: cout(以及除cout之外的其他函數)嗎? – Taylor 2013-03-08 21:31:13

+0

是'std :: cout'是正常的。在大多數C++代碼中也是正常的。如果你覺得你不能再輸入五個字符,你也可以使用'std :: cout'。但是'std ::'在一段時間後變成了一種反射。 – AxelOmega 2013-03-08 21:34:05

回答

5

gmtime()localtime()ctime()asctime()返回一個指向靜態數據。因此後續的調用可能會覆蓋以前調用寫入的信息。這也意味着這些調用不是線程安全的,雖然在這種情況下不涉及多個線程。

其他解答提供了這種限制的可能的解決方法。

2

您實際上並不想要gmtime()(請參閱Shafik's answer)。你只想要一個std::tm你可以修改:

void timeclass::tm_init(int y) { 
    timeinfo = new std::tm; 
    timeinfo->tm_year = y - 1900; 
    timeinfo->tm_mon = 0; 
    timeinfo->tm_mday = 1; 
    timeinfo->tm_hour = 0; 
    timeinfo->tm_min= 0; 
    timeinfo->tm_sec= 0; 
} 

由於沙菲克已經寫了,你的問題是你指向許多*time()方法中使用的內部靜態std::tm。因此,只要創建自己的std::tm,甚至simplier,用它作爲一個成員,而不是你指針:

class timeclass { 
    private: 
    std::tm timeinfo; 
    /* rest stays the same */ 
}; 

void timeclass::tm_init(int y) { 
    timeinfo = *std::gmtime(&rawtime); // if you need gmtime 
    timeinfo.tm_year = y - 1900; 
    timeinfo.tm_mon = 0; 
    timeinfo.tm_mday = 1; 
    timeinfo.tm_hour = 0; 
    timeinfo.tm_min= 0; 
    timeinfo.tm_sec= 0; 
} 
+0

謝謝@zeta,我會玩一下,現在我的代碼嚴重依賴於'gmtime()',所以我會盡力讓它工作。 – Taylor 2013-03-08 21:26:01

+0

@Taylor:如果你真的需要gmtime(),你仍然可以使用'std :: tm'方法,因爲'std :: tm'應該是POD:'timeinfo = * gmtime(&rawtime)'。 – Zeta 2013-03-08 21:51:19

+0

太棒了,我敢肯定它可以在沒有gmtime()的情況下工作,但這是最簡單的解決方案。由於timeinfo不再是一個指針,而且它似乎在運行,所以我拋出了一些''。我有很多能夠增加日期的能力(不包括在我上面的例子中),但是我明天就會努力工作並回來。再次感謝。 – Taylor 2013-03-08 22:08:25

0

正如其他人所指出的那樣,問題是,你正在使用的函數返回全局數據。所以你的問題一直在側面。然而,正如你指出的那樣,C++封裝在類級而不是對象級,所以任何對象都可以修改同一類的任何其他對象的私有數據。

你可以避開這個只用抽象類作爲參數和類成員:

class Time { 
public: 
    virtual void setYear(int year) = 0; 
    virtual void printDate() = 0; 
    virtual void subtract(Time& otherTime) = 0; 
}; 
+0

他們返回導致問題的全局數據,但是線程安全性與它無關(可能是線程局部全局,但問題仍然會發生)。 – GManNickG 2013-03-08 22:04:20

+0

@GManNickG好的,刪除了對線程安全的引用,謝謝。 – 2013-03-08 22:20:51