为什么C ++没有const构造函数?

时间:2011-08-04 04:20:29

标签: c++ constructor const

编辑:因为上一个例子存在缺陷而发生重大变化,这可能会使一些答案/评论看起来很奇怪)

这可能过于人为,但由于缺少const构造函数,以下内容是合法的:

class Cheater
{
public:
    Cheater(int avalue) 
       : cheaterPtr(this) //conceptually odd legality in const Cheater ctor
       , value(avalue) 
    {}

    Cheater& getCheaterPtr() const {return *cheaterPtr;}
    int value;

private:
    Cheater * cheaterPtr;
};

int main()
{
    const Cheater cheater(7); //Initialize the value to 7

    cheater.value                 = 4;    //good, illegal
    cheater.getCheaterPtr().value = 4;    //oops, legal

    return 0;
}

似乎提供一个const构造函数在技术上就像const方法一样容易,并且类似于const重载。

注意:我不是在寻找'Image( const Data & data ) const'而是'const Image( const Data & data) const'

所以:

  • 为什么const构造函数在C ++中不存在?

以下是上下文的一些相关材料:

5 个答案:

答案 0 :(得分:10)

仅仅因为Image在你想象的构造函数中是const并不意味着m_data指向的是什么。您最终能够将“指向const的指针”分配给类中的“const指向非const”,这样可以在没有强制转换的情况下删除const。这显然会让你违反不变量而且不允许。

据我所知,在当前标准范围内,可以准确,完整地指定所需的任何特定常数。

另一种看待它的方法是const表示该方法不会改变对象的状态。构造函数的唯一目的是初始化一个对象的状态是有效的(无论如何 - 希望任何具有副作用的构造函数都应该......仔细评估)。

编辑:在C ++中,constness既适用于成员,也适用于指针和引用,适用于引用对象的可访问const。 C ++有意识地决定拆分这两个不同的常量。首先,我们是否同意这个展示差异的代码应该编译并打印出“non-const”?

#include <iostream>

struct Data
{
    void non_const() { std::cout << "non-const" << std::endl; }
};

struct Image
{
     Image(             Data & data ) : m_data( data ) {}

     void check() const { m_data.non_const(); }
     Data & m_data;
};

int main()
{
    Data data;
    const Image img(data);
    img.check();

    return 0;
}

那么为了获得它可以接受const-ref并将其存储为const-ref的行为,引用的有效声明必须更改为const。这将意味着它将是一个完全不同的类型,而不是原始类型的const版本(因为具有const限定条件的成员的两个类型在C ++中被视为两个单独的类型)。因此,编译器必须能够做过多的幕后魔术来来回转换这些东西,记住成员的常量,或者它必须将它视为一个单独的类型然后不能用来代替正常类型。

我认为你想要实现的是一个referencee_const对象,这个概念只作为一个单独的类存在于C ++中(我怀疑可以通过明智地使用模板来实现,尽管我没有尝试过)。

这是一个严格的理论问题(答案:C ++决定拆分对象和引用常量)还是有一个实际的实际未解决的问题,你试图解决?

答案 1 :(得分:5)

  

它本身不是const方法

如果此构造函数本身不是const方法,那么内部指针等也不会是const。因此,它无法将const值设置为非const成员。

使其在语法上有效的唯一方法是,此构造函数需要对所有非mutable成员进行成员初始化。从本质上讲,使用此构造函数时,任何未声明为mutable的成员都将被隐式声明为const。这相当于使构造函数成为const方法;只有初始化程序才能初始化成员。构造函数的主体无法对不可变成员执行任何操作,因为那时成员将是const

你要求的是语法上可疑的。您实际上是在试图欺骗API,将常量数据存储在为可变数据设计的对象中(这就是为什么您没有将成员指针声明为const)。如果您想要对象的不同行为,则需要声明该对象具有该特定行为。

答案 2 :(得分:4)

Mark B考虑了基本考虑因素,但请注意,您可以在纯C ++中执行类似的操作。考虑:

struct Data { };

class ConstImage {
protected:
  const Data *const_data;
public:
  ConstImage (const Data *cd) : const_data(cd) { }
  int getFoo() const { return const_data->getFoo(); }
};

class Image : public ConstImage {
protected:
  Data *data() { return const_cast<Data *>(const_data); }
public:
  Image(Data *d) : const_data(d) { }
  void frob() { data()->frob(); }
};

不使用const Image *,而是使用ConstImage *,然后就可以了。您还可以简单地定义一个静态函数伪构造函数:

const Image *Image::newConstImage(const Data *d) {
  return new Image(const_cast<Data*>(d));
}

当然,这取决于程序员确保没有任何const函数可能以某种方式改变指向Data的状态。

您还可以结合使用这些技术:

class Image {
protected:
  const Data *const_data;
  Data *data() { return const_cast<Data *>(const_data); }
public:
  void frob() { data()->frob(); }
  int getFoo() const { return const_data->getFoo(); }

  Image(Data *d) : const_data(d) { }

  static const Image *newConst(const Data *cd) {
    return new Image(const_cast<Data *>(cd));
  }
};

这两个世界都是最好的;由于data()是非const成员,因此您可以静态检查指向值的变异。但是,你也有一个const构造函数,可以直接在Image *const Image *之间进行转换(即,如果你知道它是安全的,你可以删除它。)

您还可以进一步抽象出指针的分离:

template<typename T>
class ConstPropPointer {
private:
  T *ptr;
public:
  ConstPropPointer(T *ptr_) : ptr(ptr_) { }
  T &operator*() { return *ptr; }
  const T &operator*() const { return *ptr; }
  T *operator->() { return ptr; }
  const T *operator->() const { return ptr; }
};


class Image {
protected:
  ConstPropPointer<Data> data;
public:
  void frob() { data->frob(); }
  int getFoo() const { return data->getFoo(); }

  Image(Data *d) : data(d) { }

  static const Image *newConst(const Data *cd) {
    return new Image(const_cast<Data *>(cd));
  }
};

现在,如果this是const,则data变为const,并将其传播到*data。对你好吗? :)

我想最终的答案可能是这样的:为了使const构造函数有用且安全,我们需要像你在语言中内置的ConstPropPointer这样的东西。然后,允许Const构造函数从const T *分配到constprop T *。这比听起来更复杂 - 例如,它如何与vector等模板类进行交互?

所以,这是一个有点复杂的变化,但问题似乎并没有那么多。更重要的是,这里有一个简单的解决方法(ConstPropPointer可以图书馆化,静态伪构造函数很容易添加)。因此,C ++委员会可能会将其传递给更重要的事情,如果它甚至提出的话。

答案 3 :(得分:0)

在我看来,ctors没有返回型规格的事实就是这里失败的。任何其他可以想象的语法,例如

class A
{
    const A& ctor(...);
}

将是非常有价值的。例如,想象一下使用原型

调用方法的情况
void my_method(const my_own_string_class& z);

如果my_own_string_class包含来自char *的ctor,编译器可以选择 这个ctor,但由于这个ctor不允许返回一个const对象,它需要分配和复制...如果允许const返回类型,可以做

class my_own_string_class
{
    char *_ptr;
    public:
    const my_own_string_class& ctor(char *txt)
    : _ptr(txt)
    { return *this;}
 }

前提是此特殊构造仅限于创建临时实例。 (并且dtor必须是可变的;))。

答案 4 :(得分:-1)

Const对象应该初始化它们的成员变量,而const构造函数则不能这样做。