开发者

Best practice for implementing constant references in C++/CLI

In native C++ it often makes sense to return an object as a constant reference. Consider class A supplying read-only-access to an instance of class B:

class B {
public:
  int X;

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

  B(const B &b) // copy constructor
    : X开发者_JAVA技巧(b.X)
  {}

};

class A {
private:
  B &b;

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

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

Now a client has the choice to read A's B-data either very efficiently by reference, or create it's own copy if needed:

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

In either case, it's guaranteed that nobody from outside is able to change the data within A's member instance of B. So, this is a perfect solution.

An equivalent CLI construct would look like this at a first glance:

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;
  }
};

But this doesn't make very much sense, since it works in C++/CLI only. When you reference it from a different .NET language, such that C# or VB.NET, you won't see a GetB implementation at all. Ok, try this instead:

  const B ^GetB()
  {
    return b;
  }

The managed pointer is constant as expected within the same assembly:

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();

At the same time in another .NET assembly (even when using C++/CLI), the constance is lost. Indeed, the following works within a second assemply referencing the one containing class A:

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

Surprisingly, that way you have "more" access to an object from outside the assembly than from inside, because the language construct behaves differently. Is this an intended behavior?

Anyway, what's the best practice to transform the idea of a constant return object into the .NET world? How would you implement class A in CLR style, providing that class B supports a huge amount of data (too much to copy) that shouldn't be changed from outside class A?


.NET unfortunately has no notion of const-correctness. The C++/CLI compiler handles const on native types just fine, but makes it unusable with managed types.

Sorry for the bad news. I hate this as much as you do.


If you want something that feels like const in .NET then you have to create interfaces using what it is impossible to mutate the object and pass these interfaces. It is not as handy as const, but external effect is same.

BTW ... your examples are strange. First B has pointless copy constructor (because that behaves like compiler-generated one), so perhaps it should be:

class B 
{
public:
    int X;

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

First A is leaking its b member and getter is not const, so perhaps it should be:

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
};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜