在C ++ / CLI中实现常量引用的最佳实践

时间:2011-02-07 01:07:59

标签: .net reference c++-cli constants language-design

在本机C ++中,将对象作为常量引用返回通常是有意义的。考虑A类提供对B类实例的只读访问:

class B {
public:
  int X;

  B(int x)
    : X(x)
  {}

  B(const B &b) // copy constructor
    : X(b.X)
  {}

};

class A {
private:
  B &b;

public:
  A(int x)
    : b(*new B(x))
  {}

  const B &GetB()
  {
    return b;
  }
};

现在客户可以选择通过引用非常有效地读取A的B数据,或者根据需要创建自己的副本:

A a1(1);
const B &b1 = a1.GetB(); // constant reference
// b1.X = 2; // compilation error

B b2 = a1.GetB(); // through copy constructor
b2.X = 2; // ok, but doesn't affect a1

在任何一种情况下,都可以保证来自外部的任何人都无法更改A的A成员实例中的数据。因此,这是一个完美的解决方案。

乍一看,等效的CLI构造看起来像这样:

public ref class B {
public:
  int X;

  B(int x)
    : X(x)
  {}

};

public ref class A {
private:
  B ^b;

public:
  A(int x)
    : b(gcnew B(x))
  {}

  const B %GetB()
  {
    return *b;
  }
};

但这并没有多大意义,因为它仅适用于C ++ / CLI。当您从不同的.NET语言(例如C#或VB.NET)引用它时,您根本看不到GetB实现。好吧,试试这个:

  const B ^GetB()
  {
    return b;
  }

托管指针在同一程序集中按预期保持不变:

A ^a1 = gcnew A(1);

const B ^b = a1->GetB(); // ok
b->X = 2; // error C3892: you cannot assign to a variable that is const

// error C2440: 'initializing' : cannot convert from 'const B ^' to 'B ^'
B ^b = a1->GetB();

同时在另一个.NET程序集中(即使使用C ++ / CLI),constance也会丢失。实际上,下面的工作在第二部分中引用了包含A类的那个:

A ^a1 = gcnew A(1);
B ^b2 = a1->GetB();
b2->X = 2; // b->X changed within a1

令人惊讶的是,通过这种方式,您可以从程序集外部“更多”访问对象,而不是从内部访问,因为语言结构的行为方式不同。这是预期的行为吗?

无论如何,将常量返回对象的想法转换为.NET世界的最佳实践是什么?你如何实现CLR风格的A类,只要B类支持大量数据(复制太多),不应该从A类外部改变?

2 个答案:

答案 0 :(得分:4)

不幸的是,.NET没有const正确性的概念。 C ++ / CLI编译器可以很好地处理本机类型上的const,但是使托管类型无法使用它。

抱歉这个坏消息。我和你一样讨厌这个。

答案 1 :(得分:1)

如果你想在.NET中感觉像const,那么你必须使用不可能改变对象并传递这些接口来创建接口。它不像const那么方便,但外部效果是一样的。

顺便说一句......你的例子很奇怪。第一个B有无意义的复制构造函数(因为它的行为与编译器生成的一样),所以也许应该是:

class B 
{
public:
    int X;

    B( int x )
        : X( x )
    {}
};

First A正在泄漏它的b成员而getter不是const,所以也许应该是:

class A
{
private:
    B b;    
public:
    A( int x )
        : b( x )
    {}

    const B& GetB() const
    {
        return b;
    }
private:
    // block copy and assignment here
    // if that was point of reference member
};