Idiomatic Way to Declare C++ Immutable Classes

Idiomatic Way to declare C++ Immutable Classes

The way you proposed is perfectly fine, except if in your code you need to make assignment of RockSolid variables, like this:

RockSolid a(0,1);
RockSolid b(0,1);
a = b;

This would not work as the copy assignment operator would have been deleted by the compiler.

So an alternative is to rewrite your struct as a class with private data members, and only public const functions.

class RockSolid {
private:
float x;
float y;

public:
RockSolid(float _x, float _y) : x(_x), y(_y) {
}
float MakeHarderConcrete() const { return x + y; }
float getX() const { return x; }
float getY() const { return y; }
}

In this way, your RockSolid objects are (pseudo-)immutables, but you are still able to make assignments.

Idiomatic way to create an immutable and efficient class in C++

  1. You truly want immutable objects of some type plus value semantics (as you care about runtime performance and want to avoid the heap). Just define a struct with all data members public.

    struct Immutable {
    const std::string str;
    const int i;
    };

    You can instantiate and copy them, read data members, but that's about it. Move-constructing an instance from an rvalue reference of another one still copies.

    Immutable obj1{"...", 42};
    Immutable obj2 = obj1;
    Immutable obj3 = std::move(obj1); // Copies, too

    obj3 = obj2; // Error, cannot assign

    This way, you really make sure every usage of your class respects the immutability (assuming no one does bad const_cast things). Additional functionality can be provided through free functions, there is no point in adding member functions to a read-only aggregation of data members.

  2. You want 1., still with value semantics, but slightly relaxed (such that the objects aren't really immutable anymore) and you're also concerned that you need move-construction for the sake of runtime performance. There is no way around private data members and getter member functions:

    class Immutable {
    public:
    Immutable(std::string str, int i) : str{std::move(str)}, i{i} {}

    const std::string& getStr() const { return str; }
    int getI() const { return i; }

    private:
    std::string str;
    int i;
    };

    Usage is the same, but the move construction really does move.

    Immutable obj1{"...", 42};
    Immutable obj2 = obj1;
    Immutable obj3 = std::move(obj1); // Ok, does move-construct members

    Whether you want assignment to be allowed or not is under your control now. Just = delete the assignment operators if you don't want it, otherwise go with the compiler-generated one or implement your own.

    obj3 = obj2; // Ok if not manually disabled
  3. You don't care about value semantics and/or atomic reference count increments are ok in your scenario. Use the solution depicted in @NathanOliver's answer.

How to idiomatically define assignment operator for immutable classes?

Assuming that you are not going to reassign your members (using const_cast etc.), I would suggest to explicitly mention in your code that you are not using operator =.

In Current C++ standard, make it private and unimplemented:

class Binary
{
//...
private:
Binary& operator = (const Binary&);
};

In upcoming C++0x standard, delete it:

class Binary
{
//...
Binary& operator = (const Binary&) = delete;
};

Creating immutable instances and modifying copies in an idiomatic way

You are not including a final else clause. Thus the type of c is Any -- the only type that is supertype both of Container and Unit, where Unit is the result of not including a catch-all else clause. If you try to force the result type to be Container, by writing c: Container =, the compiler now tells you the missing else clause resulting in Unit is not assignable to Container.

Thus

val c = if (abs(b.amount) >= max) {
b.copy(amount = max, condition = "Goodbye")
} else if (abs(b.amount) < max) {
b.copy(amount = abs(b.amount))
} else b // leave untouched !

works. The compiler isn't smart enough to figure out that the last else clause cannot be logically reached (it would need to know what abs and >= and < means, that they are mutual exclusive and exhaustive, and that abs is purely functional, as is b.amount).

In other words, since you know that the two clauses are mutually exclusive and exhaustive, you can simplify

val c = if (abs(b.amount) >= max) {
b.copy(amount = max, condition = "Goodbye")
} else { // i.e. abs(b.amount) < max
b.copy(amount = abs(b.amount))
}

Immutable classes and memory in C++

In the case you've described, the assignment operator of MyImmutableClass is called to copy the right hand side of the assignment to the left hand side (overwriting whatever was on the left hand side). If your class really is immutable, it won't even have an accessible assignment operator, and you will get a compile error.

If your object is mutable, and you do have a (correct) assignment operator, then there will be no memory leak because destructors for data on the left hand side will run as necessary to release memory. Writing a correct assignment operator can be tricky for some classes, though.

This concept of assigning directly to a (user-defined) object does not exist in Java, since over there everything is a reference.

What is the most concise form of an immutable data class in C#?

This might not be the most concise way of writing it, but there are a number of differences between using readonly backing fields and private set auto-properties:

  • private set implies that the class implementation can alter the value of the property.
  • readonly backing field can only be initialized from the constructor; the class implementation cannot alter its value.
  • readonly backing field better communicates the intent, if that intent is to make something immutable.
  • backing fields with a _namingConvention make the this qualifier redundant.
  • get-only property with a readonly backing field is inherently thread-safe; more information here.
public class Neighborhood
{
public Neighborhood(State left, State center, State right)
{
_left = left;
_center = center;
_right = right;
}

private readonly State _left;
public State Left { get { return _left; } }

private readonly State _center;
public State Center { get { return _center; } }

private readonly State _right;
public State Right { get { return _right; } }
}

What is the idiomatic F# equivalent to declaring a variable for later use in C#

You can use functions and values. There is no need to declare unassigned variables.

let makeTestClass() =
MyTestClass()
// etc.

let ``First test``() =
let sut = makeTestClass()
sut.DoSomeWork()

let ``Second test``() =
let sut = makeTestClass()
sut.DoSomeWork()

How to make struct immutable inside a class definition

It's a little difficult to give a complete answer without understanding exactly what you are trying to accomplish, but I'll start with a few important distinctions.

First, in C#, the struct/class distinction isn't about mutability per se. You can have a immutable class, like this one

public class CannotBeMutated
{
private string someVal;
public CannotBeMutated(string someVal)
{
_someVal = someVal
}

public string SomeVal => _someVal;
}

and a mutable struct, like this one

// This is not at all idiomatic C#, please don't use this as an example
public struct MutableStruct
{
private string _someVal;
public MutableStruct(string someVal)
{
_someVal = someVal;
}

public void GetVal()
{
return _someVal
}

public void Mutate(string newVal)
{
_someVal = newVal;
}
}

Using the above struct I can do this

 var foo = new MutableStruct("Hello");
foo.mutate("GoodBye");
var bar = foo.GetVal(); // bar == "GoodBye"!

The difference between structs and classes is in variable passing semantics. When an object of a value type (e.g. a struct) is assigned to a variable, passed as a parameter to or returned from a method (including a property getter or setter) a copy of the object is made before it is passed to the new function context. When a object of a reference type is passed as a parameter to or returned from a method, no copy is made, because we only pass a reference to the object's location in memory, rather than a copy of the object.

An additional point on struct 'copying'. Imagine you have a struct with a field that is a reference type, like this

public struct StructWithAReferenceType
{
public List<string> IAmAReferenceType {get; set;}
}

When you pass an instance of this struct into a method, a copy of the reference to the List will be copied, but the underlying data will not. So if you do

public void MessWithYourSruct(StructWithAReferenceType t)
{
t.IAmAReferenceType.Add("HAHA");
}

var s = new StructWithAReferenceType { IAmAReferenceType = new List()};
MessWithYourSruct(s);
s.IAmAReferenceType.Count; // 1!

// or even more unsettling
var s = new StructWithAReferenceType { IAmAReferenceType = new List()};
var t = s; // makes a COPY of s
s.IAmAReferenceType.Add("hi");
t.IAmAReferenceType.Count; // 1!

Even when a struct is copied, its reference type fields still refer to the same objects in memory.

The immutable/mutable and struct/class differences are somewhat similar, insofar as they are both about where and whether you can change the contents of an object in your program, but they are still very distinct.

Now on to your question. In your second example, Class1 is not immutable, as you can mutate the value of Struct2 like this

var foo = new Class1();
foo.Struct2 = new Struct2("a", 1);
foo.Struct2 // returns a copy of Struct2("a", 1);
foo.Struct2 = new Struct2("b", 2);
foo.Struct2 // returns a copy of Struct2("b", 2);

Struct2 is immutable, as there is no way for calling code to change the values of StrVar or InVar once. InStruct is similarly immutable. However, Array is not immutable. So InStruct is an immutable container for a mutable value. Similar to if you had a ImmutableList<List<string>>. While you can guarantee calling code does not change the value of InStruct.StrArray to a different array, you can do nothing about calling code changing the value of the objects in the Array.

Finally, some generic advice related to your example.

First, mutable structs, or structs with mutable fields, are bad. The examples above should point to why structs with mutable fields are bad. And Eric Lippert himself has a great example of how terrible mutable structs can be on his blog here

Second, for most developers working in C# there's almost never a reason to create a user defined value type (i.e. a struct). Objects of value types are stored on the stack, which makes memory access to them very fast. Objects of reference types are stored on the heap, and so are slower to access. But in the huge majority of C# programs, that distinction is going to be dwarfed by the time cost of disk I/O, network I/O, reflection in serialization code, or even initialization and manipulation of collections. For ordinary developers who aren't writing performance-critical standard libraries, there's almost no reason to think about the performance implications of the difference. Heck, developers in Java, Python, Ruby, Javascript and many other languages get by in languages totally without user-defined value types. Generally, the added cognitive overhead they introduce for developers is almost never worth any benefit you might see. Also, remember that large structs must be copied whenever they are passed or assigned to a variable, and can actually be a performance problem.

TL;DR you probably shouldn't use structs in your code, and they don't really have anything to do with immutability.

Why do we need immutable class?

The other answers seem too focused on explaining why immutability is good. It is very good and I use it whenever possible. However, that is not your question. I'll take your question point by point to try to make sure you're getting the answers and examples you need.

I am unable to get what are the scenarios where we need an immutable class.

"Need" is a relative term here. Immutable classes are a design pattern that, like any paradigm/pattern/tool, is there to make constructing software easier. Similarly, plenty of code was written before the OO paradigm came along, but count me among the programmers that "need" OO. Immutable classes, like OO, aren't strictly needed, but I going to act like I need them.

Have you ever faced any such requirement?

If you aren't looking at the objects in the problem domain with the right perspective, you may not see a requirement for an immutable object. It might be easy to think that a problem domain doesn't require any immutable classes if you're not familiar when to use them advantageously.

I often use immutable classes where I think of a given object in my problem domain as a value or fixed instance. This notion is sometimes dependent on perspective or viewpoint, but ideally, it will be easy to switch into the right perspective to identify good candidate objects.

You can get a better sense of where immutable objects are really useful (if not strictly necessary) by making sure you read up on various books/online articles to develop a good sense of how to think about immutable classes. One good article to get you started is Java theory and practice: To mutate or not to mutate?

I'll try to give a couple of examples below of how one can see objects in different perspectives (mutable vs immutable) to clarify what I mean by perspective.

... can you please give us any real example where we should use this pattern.

Since you asked for real examples I'll give you some, but first, let's start with some classic examples.

Classic Value Objects

Strings and integers are often thought of as values. Therefore it's not surprising to find that String class and the Integer wrapper class (as well as the other wrapper classes) are immutable in Java. A color is usually thought of as a value, thus the immutable Color class.

Counterexample

In contrast, a car is not usually thought of as a value object. Modeling a car usually means creating a class that has changing state (odometer, speed, fuel level, etc). However, there are some domains where it car may be a value object. For example, a car (or specifically a car model) might be thought of as a value object in an app to look up the proper motor oil for a given vehicle.

Playing Cards

Ever write a playing card program? I did. I could have represented a playing card as a mutable object with a mutable suit and rank. A draw-poker hand could be 5 fixed instances where replacing the 5th card in my hand would mean mutating the 5th playing card instance into a new card by changing its suit and rank ivars.

However, I tend to think of a playing card as an immutable object that has a fixed unchanging suit and rank once created. My draw poker hand would be 5 instances and replacing a card in my hand would involve discarding one of those instance and adding a new random instance to my hand.

Map Projection

One last example is when I worked on some map code where the map could display itself in various projections. The original code had the map use a fixed, but mutatable projection instance (like the mutable playing card above). Changing the map projection meant mutating the map's projection instance's ivars (projection type, center point, zoom, etc).

However, I felt the design was simpler if I thought of a projection as an immutable value or fixed instance. Changing the map projection meant having the map reference a different projection instance rather than mutating the map's fixed projection instance. This also made it simpler to capture named projections such as MERCATOR_WORLD_VIEW.



Related Topics



Leave a reply



Submit