在构造函数之后初始化C++ const字段

Ela*_*ich 8 c++ constructor const

我想创建一个不可变的数据结构,例如,可以从文件初始化.

class Image {
public:
   const int width,height;
   Image(const char *filename) {
     MetaData md((readDataFromFile(filename)));
     width = md.width();   // Error! width  is const
     height = md.height(); // Error! height is const
   }
};
Run Code Online (Sandbox Code Playgroud)

我能做些什么来解决这个问题

class Image {
   MetaData md;
public:
   const int width,height;
   Image(const char *filename):
     md(readDataFromFile(filename)),
     width(md.width()),height(md.height()) {}
};
Run Code Online (Sandbox Code Playgroud)

然而

  1. 它迫使我将MetaData保存为我对象中的一个字段.我并不总是想要.
  2. 有时构造函数中的逻辑比单个读取要复杂得多(例如,错误处理可能需要几行)

所以我想到的唯一解决方案是沿着这条线

class A {
  int stub;
  int init(){/* constructor logic goes here */}
  A():stub(init)/*now initialize all the const fields you wish
  after the constructor ran */{}
};
Run Code Online (Sandbox Code Playgroud)

有更好的主意吗?(在Java,你可以final在构造函数中初始化s).

Geo*_*che 11

你可以移动widthheight进入一个类型和初始化代码进入初始化辅助函数:

// header:
struct Size { 
    int width, height;
    Size(int w, int h) : width(w), height(h) {}
};

class Image {
    const Size size; // public data members are usually discouraged
public:
    Image(const char *filename);
};

// implementation:
namespace {
    Size init_helper(const char* filename) {
        MetaData md((readDataFromFile(filename)));
        return Size(md.width(), md.height());
    }
}

Image::Image(const char* filename) : size(init_helper(filename)) {}
Run Code Online (Sandbox Code Playgroud)


Mat*_* M. 7

你可以在NamedConstructor这里简单地使用这个成语:

class Image
{
public:
  static Image FromFile(char const* fileName)
  {
    MetaData md(filename);
    return Image(md.height(), md.width());
  }

private:
  Image(int h, int w): mHeight(h), mWidth(w) {}

  int const mHeight, mWidth;
};
Run Code Online (Sandbox Code Playgroud)

命名构造函数的一个主要优点是它们的显而易见性:名称表示您正在从文件构建对象.当然它稍微冗长一点:

Image i = Image::FromFile("foo.png");
Run Code Online (Sandbox Code Playgroud)

但这从来没有困扰过我.


M. *_* E. 5

如果是 C++0x,我会推荐这个(委托构造函数):

class Image
{
  public:

    const int width, height;

    Image(const char* filename) : Image(readDataFromFile(filename)) { }
    Image(const MetaData& md) : width(md.width()), height(md.height()) { }
};
Run Code Online (Sandbox Code Playgroud)


cel*_*vek 2

您可以放弃构造函数中的常量:

class Image {
public:
    const int width,height;
    Image(const char *filename) : width(0), height(0) {
        MetaData md(readDataFromFile(filename));

        int* widthModifier = const_cast<int*>(&width);
        int* heightModifier = const_cast<int*>(&height);
        cout << "Initial width " << width << "\n";
        cout << "Initial height " << height << "\n";
        *widthModifier = md.GetWidth();
        *heightModifier = md.GetHeight();
        cout << "After const to the cleaners " << width << "\n";
        cout << "After const to the cleaners " << height << "\n";
    }
};
Run Code Online (Sandbox Code Playgroud)

这将实现你想做的事情,但我必须说我个人会远离它,因为它会导致根据标准的未定义行为(摘自cppreference

const_cast 可以形成对实际上引用 const 对象的非常量类型的引用或指针...通过非常量访问路径修改 const 对象...会导致未定义的行为。

我担心任何公共数据成员(至少在您的特定示例中)。我会采用 Georg 的方法,或者将数据设为私有并仅提供吸气剂。

  • @GMan,我不在乎它是否是Java。将“width”和“height”设置为“const”是一个好主意,“Java”、“Scala”、“Haskell”、“C”或“C++”。在“Java”中,这更容易做到,在“C++”中,你必须经历一些困难。我真的不认为 const 字段的范例是 Java 特定的或依赖于“Java”的特殊特征。 (2认同)