Taylor
Taylor

Reputation: 59

Private class members not fully encapsulated?

This is a follow up post from Instance-level encapsulation with C++.

I've defined a class and created two objects from that class.

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

This example is part of a date counter sliced and diced from my main program, but it illustrates my point. I want to have a date counter running for each instance of the class (time1 and time2), but it looks like once I construct the time2 object, the 'timeinfo' variable that I thought was encapsulated in time1 gets overwritten by the time2 constructor.

I am aware that C++ supports only class-level encapsulation, and am wondering if my problem is because members of the same class have access to one another's private members. Is there a way around this, so I can achieve what I want to do? Thank you, Taylor

Upvotes: 1

Views: 260

Answers (3)

Shafik Yaghmour
Shafik Yaghmour

Reputation: 158529

gmtime(), localtime(), ctime() and asctime() return a pointer to static data. So subsequent calls may overwrite information written by previous calls. This also means that these calls are not thread-safe although in this case multiple threads are not involved.

Other answers have provided possible workarounds for this limitation.

Upvotes: 5

Open AI - Opting Out
Open AI - Opting Out

Reputation: 24143

As others have pointed out, the problem is that the functions you are using return global data. So your question has been side-stepped.

However, as you point out, C++ encapsulates at the class level rather than object level, so any object can modify private data of any other object of the same class.

You can get round this by only using abstract classes as parameters and class members:

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

Upvotes: 0

Zeta
Zeta

Reputation: 105895

You don't actually want gmtime() (see Shafik's answer). You just want a std::tm you can modify:

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

As Shafik already wrote, your problem is the internal static std::tm used by many *time() methods which you point to. So just create your own std::tm, or even simplier, use it as a member instead of your pointer:

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

Upvotes: 2

Related Questions