2013-03-08 3 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, переменная «timeinfo», которая, как я думала, была инкапсулирована во время1, перезаписывается конструктором time2.

Я знаю, что C++ поддерживает только инкапсуляцию на уровне класса, и мне интересно, связана ли моя проблема с тем, что члены одного класса имеют доступ к частным членам друг друга. Есть ли способ обойти это, чтобы я мог достичь того, что хочу? Спасибо, Тейлор

+1

Избегайте делать 'используя патезрасе;'. См. [Здесь] (http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-a-bad-practice-in-c) для объяснения. – AxelOmega

+0

Спасибо @AxelOmega, я приветствую любые намеки, так как я не эксперт в этом. Вы предложили бы исключить «using namespace std;» вообще, а затем просто вызвать std :: cout (и другие функции, кроме cout) явно? – Taylor

+0

Да 'std :: cout' - это нормально. Это то же, что и в большинстве кодов C++. Вы также можете использовать 'using std :: cout', если вы чувствуете, что не можете набрать еще пять символов. Но 'std ::' становится рефлексом через некоторое время. – AxelOmega

ответ

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; 
} 

Как Шафик уже писал, ваша проблема является внутренним статическим std::tm используется многими *time() методами, которые вы указывают. Так просто создать свой собственный std::tm, или даже проще, использовать его в качестве члена вместо указателя:

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

+0

@Taylor: Если вам действительно нужно 'gmtime()', вы все равно можете использовать метод 'std :: tm', так как' std :: tm' должен быть POD: 'timeinfo = * gmtime (& rawtime)'. – Zeta

+0

отлично, я уверен, что это можно заставить работать без 'gmtime()', но это самое простое решение. Я выбрал несколько «и», так как timeinfo больше не является указателем и, похоже, работает.Где-то у меня есть возможность увеличить свою дату (не включенную в мой пример выше), но я буду работать над этим завтра и вернуться. Еще раз спасибо. – Taylor

0

Как уже отмечалось, проблема заключается в том, что функции, которые вы используете возвращают глобальные данные. Таким образом, ваш вопрос был побочным.

Однако, как вы указываете, C++ инкапсулирует на уровне класса, а не на уровне объекта, поэтому любой объект может изменять личные данные любого другого объекта того же класса.

Вы можете обойти это, используя только абстрактные классы в качестве параметров и членов класса:

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

Они возвращают глобальные данные, которые вызывают проблему, но безопасность потоков не имеет к этому никакого отношения (может быть глобальным потоком, но проблема все равно будет возникать). – GManNickG

+0

@GManNickG Хорошо, удаленная ссылка на безопасность потоков, спасибо. –

Смежные вопросы