C++ C++;等价于java最终成员数据

C++ C++;等价于java最终成员数据,c++,class-design,C++,Class Design,首先,我的最新代码是Java,我不想“用C++编写Java” 这里的交易,我必须创建一个不可变的类。这相当简单。唯一的问题是获取初始值需要一些工作。所以我不能简单地调用initializes来初始化我的成员 那么,创建这样一个类的最佳方式是什么?我如何在C++标准中将我的不可变的/最终的属性暴露给外部世界? 下面是一个示例类: class Msg { private: int _rec_num; int _seq; string text;

首先,我的最新代码是Java,我不想“用C++编写Java”

这里的交易,我必须创建一个不可变的类。这相当简单。唯一的问题是获取初始值需要一些工作。所以我不能简单地调用initializes来初始化我的成员

那么,创建这样一个类的最佳方式是什么?我如何在C++标准中将我的不可变的/最终的属性暴露给外部世界? 下面是一个示例类:

class Msg {
    private:
        int _rec_num;
        int _seq;
        string text;
    public:
        Msg(const char* buffer) {
            // parse the buffer and get our member here...
            // ... lots of code
        }

        // does this look like proper C++?
        int get_rec_num() { return _rec_num; }
    };

在终结器上

没有,你必须模仿它。可以使用清理功能,也可以将所有资源封装在类中。编译器将在应用程序中放置静态机制来调用RAII类上的析构函数——即,当它们超出范围时,资源将通过析构函数释放

关于不变性和初始化

通常情况下,如果某个对象是不可变的,并且该类的所有成员都是常量,那么只有在初始化该类时才能“写入”它们。但是,在您的情况下,这可能是不可能的


我建议您收集所有资源,并在拥有这些资源后初始化类(通过带有const成员的非默认构造函数)。另一种选择(我不同意)是使用一个mutator函数,该函数“声称”是const-correct,但会写入const值以进行一次性构造后初始化

您走的是正确的道路——对所有事情都使用getter,没有任何setter,您的类实际上是不可变的

不过,不要忘记一些常见情况——您可能希望将operator=()方法声明为private,而不是实现它,这样就不会有人用默认编译器生成的赋值运算符重写对象,等等

    // does this look like proper C++?
    int get_rec_num() { return _rec_num; }
你应该使用

    int get_rec_num() const { return _rec_num; }

(请参阅
const
,它允许调用const对象上的成员)。

我会将不可变成员标记为“const”,并在构造函数初始值设定项列表中为其赋值

我还将在类之外解析缓冲区,并将字符串传递给构造函数

大概是这样的:

class Msg {
  private:
    int _rec_num;
    int _seq;
    const std::string text;
  public:
    Msg(const std::string& str) :
      text(str)
    {

    }

    // does this look like proper C++?
    int get_rec_num() const { return _rec_num; }
};

// parse the buffer and get our parameter somewhere else
注意:

您应该将任何不会更改类内部状态的成员函数设置为“const”;因为这将允许您使用const对象调用它们


您应该避免在头文件中包含using std::string;因为任何包含标题的人都会被强制使用。

C++提供了一些很好的机制来使类不可变。你必须做的是:

  • 声明所有公共(可能是受保护的)方法
    const
  • 运算符=
    声明(但不定义)为私有
这将确保对象在创建后无法修改。现在,您可以使用const方法以任何方式提供对现在不可变的数据成员的访问。您的示例看起来是正确的,前提是您将其设置为常量
const

int get_rec_num() const { return _rec_num; }
编辑:由于
C++11
,您可以显式删除
运算符=
,而不仅仅是不定义它。这明确指示编译器不要定义默认的复制赋值运算符:

Msg& operator=(const Msg&) = delete;

要使变量不可变,必须使用
const
关键字eg
const int\u rec\u num
。常量变量只能通过初始化,在构造函数中的任何代码之前调用。这意味着您不能在设置常量成员变量的构造函数中进行任何处理

有两种方法可以解决这个问题,首先可以创建另一个内部类,该类接收缓冲区并将其解析为变量。将此内容的
const
版本放入MSG类,并将其放入初始化列表:

class MsgInner
{
    public:
    int _rec_num;

    Msg(const char* buffer) {
        // Your parsing code
    }
};

class Msg
{
    public:
    const MsgInner msg;

    Msg(const char* buffer) : msg(buffer)
    { // any other code }
};

这也许不是那么“标准”,但这是另一种观点。否则,您也可以使用get方法按照其他答案的建议进行操作

首先,可以在构造时高效地初始化成员,即使它们被声明为const(这既不必要也不推荐)

我仍然建议您将此类拆分为两个单独的类(伪代码):


这也很好地将保存和解析数据分为不同的类。

实际上应该是“const int get_rec_num()const…”如果按值返回,有什么区别?只要引用不返回,他的类将是不可变的。这是一个很好的惯例,如果我们现在强调它,OP就会学到另一个概念。@Hassan:我支持pmr。那
const
一无所获。如果(将来)用int封装类替换它——比如
struct Integer{byte[16]\u data},那么它就没有意义了--程序员不应该编写一致的代码吗?。在C++中懒惰并不是副作用。实际上,它应该是“const int GETReCurnNo.No()const……- Hassan SyedIt没有什么区别,因为对于内置类型,没有常量rof值。无论返回类型是
int
还是
const int
,都不能写入
get\u rec\u num()=7
。我是错了还是最终调用的终结器只是一个残废的析构函数?(假设您使用良好的内存管理,根据需要使用对象优先权或智能指针)。@martin我认为这是有道理的——语法糖;当我在打字时,我自己也有了这个顿悟。然而,它比使用RAII更方便程序员。当我还是一个年轻的程序员时,我渴望C++中的一个终结器。马丁:你的意思是java?如果是这样,那么终结器就不是最终被调用的残缺析构函数。它是一个伪析构函数,最终可能会被调用,也可能不会被调用。而且它不仅仅是“跛脚”——实际上它可以做C++析构函数不能(有效)做的事情,最重要的是
// Msg: pure data object; copy constructible but not modifiable.
class Msg
{
public:
  Msg(int rec_num, ...)
    : rec_num_(rec_num)
    ...
  {}

  int rec_num() const
  { return rec_num_; }
  ...
private:
  // prevent copying
  Msg& operator=(Msg const&);
private:
  int rec_num_;
}; 

// MsgParser: responsible for parsing the buffer and
// manufacturing Msg's.
class MsgParser
{
public:
  static Msg Parse(char const* buffer)
  {
     ... parse ...
     return Msg(...);
  }
};

// Usage
Msg const msg = MsgParser::Parse(buffer);