【问题标题】:Is it possible to construct only const objects of specific type in C++?是否可以在 C++ 中仅构造特定类型的 const 对象?
【发布时间】:2014-03-17 02:03:11
【问题描述】:

我想实现具有以下属性的类:

class A { ... };

const A a;  // ok - should work.
A b;        // compilation error - shouldn't work!

另外,如果一个对象的constness 依赖于构造函数签名会更好:

const A c(1);  // ok - should work.
A d("a");      // ok - should work.
A e(2);        // compilation error - shouldn't work!

如果需要,允许使用 C++11。


更新 #1

由于我不知道答案,所以不需要严格遵循上面的代码 - 欢迎任何提供类似语义的 C++ 模式。

【问题讨论】:

  • 不能,但是可以让所有成员函数const
  • 我记得我认为为此目的使用 const 构造函数会很有用,尽管我不再记得我的用例了。与复制与不复制某些内部数据有关。
  • 老实说,我真的不明白这与班级有什么关系。您可以使成员函数和成员数据const 足以满足所有需求。
  • @OP 我不知道有什么方法可以临时做到这一点,但你能解释一下你所问的和不可变类的非常量实例之间的区别(所有方法都是 const ,没有非常量、非私有数据)。
  • 如果您将A 类型设为私有并禁止复制和移动,则只能绑定对其的引用。然后,您可以提供 make_A 函数,这些函数根据参数类型返回 const 和非 const As。 Live example(通过其他 make_A 重载可以允许显式复制和显式移动。)

标签: c++ c++11 constructor constants


【解决方案1】:

您可以使用一个额外的构造函数参数来实现这一点,该参数是对 self 的引用,例如:

class X {
public:
    X(X const& self) {
        assert(this == &self);
    }

private:
    X(X&);
};

然后像这样调用它:

X const x(x); // works
X y(y); // fails to compile
X z(x); // fails at run-time

【讨论】:

  • @stefan 为什么不呢?它被定义为this 是。
  • 指针已定义,但对象尚未初始化...但是,您需要 assert() 的事实将这一壮举限制为运行时检查而不是编译时检查(OP要求进行编译时检查。)用几个模板说,您可以用编译时检查替换您的断言。
【解决方案2】:

1.您可以创建只有 const 方法和私有成员的类。

2.您可以创建“普通”类,但将其构造函数声明为私有。那么你将需要一个具有以下方法(或类似方法)的朋友类

class ConstClassProvider{
public:
    static const A* getA(/* you can have params here*/)
    {
        return new A();
    }
}

所以

A a1;//error
const A a2;//error
A *a3 = ConstClassProvider::getA(); //error
const A *a4 = ConstClassProvider::getA(); //ok!

【讨论】:

  • 谢谢。我在没有 IDE 或编译器的情况下打字。
  • 哎哟... 请不要返回具有隐含所有权的原始指针; 请改用unique_ptr
  • 这只是一个简单的示例。
【解决方案3】:

这可能就是你要找的:

class AData {
public:
    AData() : intValue( 0 ), stringValue( 0 ) {}
    void SetInt( int arg ) { intValue = arg; }
    void SetString( const char* arg ) { stringValue = arg; }

private:
    int intValue;
    const char* stringValue;
};

class A {
public:
    A();
    void Init( int intValue ) const;
    void Init( const char* stringValue );

private:
    AData* p;
};

A::A() : p( new AData )
{
}

void A::Init( int intValue ) const
{
    p->SetInt( intValue );
}

void A::Init( const char* stringValue )
{
    p->SetString( stringValue );
}

【讨论】:

  • 对不起,Int 和 String 被交换了:你会得到 const A a 的编译器错误; a.Init("qwerty");
【解决方案4】:

你需要创建一个不可变的类。换句话说,使用封装来防止您的类的用户设置任何字段。

基本上:

class Immutable{
private:
  const int intField;
  const std::string textField;
public:
  Immutable(const std::string& ref, int copy) : intField{copy}, testField{ref} {}
  int getIntField(){return intField;}
  const std::string& getTextField(){ return textField; }
}

那就不要通过 setter 暴露你的内部结构。

【讨论】:

  • 可以,但与签名无关。
  • @user2672165 - 我不明白你的反对意见。你能扩展吗?
  • 不清楚抱歉。我的意思是无论构造函数签名如何,此类都将始终为 const。
  • 它可以根据某种注入策略在运行时很容易地工作,但在编译时却不行(据我所知)。另一种方法是让两个内部类公开不同的函数集,这些函数可以在外部类成员上执行,但在概念上与两个派生类相同。
猜你喜欢
  • 2021-09-14
  • 1970-01-01
  • 2014-05-31
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多