【问题标题】:Private class members not fully encapsulated?私有类成员没有完全封装?
【发布时间】:2023-03-18 12:14:02
【问题描述】:

这是来自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++ 仅支持类级封装,我想知道我的问题是否是因为同一类的成员可以访问彼此的私有成员。有没有办法解决这个问题,所以我可以实现我想做的事情?谢谢, 泰勒

【问题讨论】:

  • 避免使用using namespace std;。有关说明,请参阅 here
  • 感谢@AxelOmega,我欢迎任何提示,因为我不是这方面的专家。您是否建议完全省略 using namespace std;,然后直接显式调用 std::cout(和 cout 以外的其他函数)?
  • 是的std::cout 是正常的。这在大多数 C++ 代码中也是正常的。如果您觉得不能再输入五个字符,您也可以使用using std::cout。但是std::会在一段时间后成为一种反射。

标签: c++ encapsulation


【解决方案1】:

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

    正如 Shafik 已经写的那样,您的问题是您指向的许多 *time() 方法使用的内部静态 std::tm。所以只需创建自己的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;
    }
    

    【讨论】:

    • 谢谢@zeta,我会玩一会儿,现在我的代码严重依赖gmtime(),所以我会努力让它工作。
    • @Taylor:如果你真的需要gmtime(),你仍然可以使用std::tm 方法,因为std::tm 应该是POD:timeinfo = *gmtime(&amp;rawtime)
    • 太棒了,我相信没有gmtime() 也可以正常工作,但这是最简单的解决方案。由于 timeinfo 不再是指针,我已经抛出了一些&amp;,它似乎在运行。在某个地方,我有很多能力增加我的日期(不包括在我上面的例子中),但我明天会继续努力,然后回来。再次感谢。
    【解决方案3】:

    正如其他人所指出的,问题在于您使用的函数返回全局数据。所以你的问题被回避了。

    但是,正如您所指出的,C++ 在类级别而不是对象级别进行封装,因此任何对象都可以修改同一类的任何其他对象的私有数据。

    你可以通过只使用抽象类作为参数和类成员来解决这个问题:

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

    【讨论】:

    • 它们返回导致问题的全局数据,但线程安全与它无关(可能是线程局部全局,但问题仍然存在)。
    猜你喜欢
    • 2014-10-21
    • 1970-01-01
    • 2011-05-15
    • 1970-01-01
    • 2019-12-29
    • 2020-11-09
    • 2011-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多