Value semantics

There is a couple of things that make C++ unique among other contemporary mainstream programming languages. One of these things is value semantics. This feature is easily missed by programmers that come into C++ from heavy-OO world, especially that there is no language keyword associated with it. In this post I will try to describe what it is and how it can be useful.

What is a value? What is an object?

The concept of a value has been exhaustively described in Elements of Programming by Alexander Stepanov and Paul McJones. It is hard to define a value, but we can show the meaning by examples. In an algebraic expression of the form x ≤ y, x and y are values. We may not know what the value x is, but we know this value is fixed and will always be the same value, e.g. 2. On the other hand, in a piece of code x <= y, x is not a value, because it may be 2 at one time, and 4 just a second later. x is an object that can hold different values at different times. This definition of an object is different than the one from OO world, where object denotes something that has methods and is an instance of a class. In the context of C++ an object is a piece of memory that has an address, a type, and is capable of storing values. The same sequence of bits stored in two objects may be interpreted as different values if the two objects’ types differ; e.g., the sequence of bits 1000001 can be interpreted as number 65 if object’s type is short int, or as character ‘A’ if object’s type is char.

The distinction into values and objects is somewhat similar to the original distinction into r-values and l-values:

lval = rval;

An l-value, the one on the left-hand side of the assignment, needs to name an object because we will be changing the value the object stores. In the case of an r-value, the one on the right-hand side of the assignment, we only require that it names a value: we will not change it, we only need to copy it. As in this instruction:

x = 2;

We must be able to query x for its address (because under this address we will store a new value), but we do not have to query the literal 2 for address.

Value semantics

Value semantics is the programming style, or the way of thinking, where we focus on values that are stored in the objects rather than objects themselves. The objects are only used to convey values: we do not care about object’s identity.

Complex mulAdd( Complex a, Complex b, Complex c )
{
  Complex ans = a * b + c;
  return ans;
}

Complex z = mulAdd(g, h, i); 

Here, the computed value of a complex number was stored in automatic object ans, then the value was (or could be) transferred to a temporary, and then to another automatic object: z. In fact, it might not have been this many objects because of optimizations known as copy elision; but this does not matter at all, because even though we need objects to convey values, we do not care about the objects, and how many of them there were, and where they were. We will be only be interested in the computed value.

In contrast to value semantics, sometimes we are very interested in the very object, and we want to operate exactly on this object, at exactly this address:

ostream & operator<<( ostream & out, Complex c )
{
    return out << c.real << ' ' << c.imag;
}

Here, the stream may be a global which allocated resources necessary for writing text into terminal, and we need to return the reference to the very same object. This way of programming style can be called reference semantics. However, note that we passed c by value, because in that case, again, it is only value that matters. In C++ we can mix value and reference semantics.

Why we use value semantics

First, you get value semantics by default. When declaring function arguments or return values, if you specify only the type name (like int) you get value semantics (you pass and return by value). If you wan to use reference semantics, you must make an extra effort to add a reference or pointer type symbol.

Second, we use value semantics in function declarations, because it closely follows the notation and reasoning from mathematics. In mathematics you operate on values. For instance, you define a function as follows:

    f: int × int → int
    f(x, y) = x·x + y·y

This is very similar to:

int f( int x, int y )
{
  return x * x + y * y;
}

Or even a bit more similar with the new function notation:

auto f( int x, int y ) -> int
{
  return x * x + y * y;
}

Third, we do not run into any memory management issues. No dangling references to nonexistent objects, no expensive and unnecessary free store allocation, no memory leaks, no smart or dumb pointers. The support for value semantics in C++ — passing variables by value — eliminates all those problems.

Fourth, we avoid any reference aliasing problems. Andrew Koenig has neatly illustrated the problem of reference aliasing in this article. In multi-threaded environment passing by value and ensuring that each thread has its own copy of the value helps avoid any unnecessary data races. Then you do not need to synchronize on such values, and the program runs faster, and is safer because it avoids any deadlocks.

Fifth, for referential transparency. This means that you get no surprises where your data is modified behind the scenes without you being able to see that. For instance, consider this example from Dave Abrahams:

Matrix x = y;
y[0][0] = 3.14; // (*)

The value of x is not modified after the assignment in the second line. The objects are separated from one another. There is no connection between them anymore. This is not so for many languages that enforce OO programming model.

You can also observe referential transparency by comparing the following two ways of advancing an STL iterator:

i = next(i, 1); // (1)
advance(i, 1);  // (2)

Function std::next does not modify anything. It just takes one iterator pointing to one element and returns another iterator that points to the next element. If you called it three times instead of one it wouldn’t change the behaviour of the program at all:

i = (next(i, 1), next(i, 1), next(i, 1));

In other words, std::next does not have any side effects. In contrast, function std::advance changes the very object it is called with. If we called it three times instead of one, we would alter program behaviour by advancing the iterator further and further. Now, what if we wanted to advance the iterator by two? Forget for a moment that in each case you can pass 2 as the second argument to do that, and for the example’s sake let’s do it with 1s. In the value semantic approach we compose function calls:

i = next(next(i)); 

Using reference semantics and imperative approach, we repeat the commands:

advance(i, 1);
advance(i, 1);

The value-semantic approach isn’t entirely pure (in functional programming sense) because in the end we do modify i with an assignment. But it somehow separates the pure parts from impure ones.

Why we do not use value semantics

Whether we pass by value or by reference often depends on a programming style. For one instance, if you think in OO terms, where you want to create an object, and let it live for a significant amount of time, and want different parties to modify the object, you need to pass by reference. This is the case for globals like std::cout that need to be accessible from different places in the code, and it always has to be this very object at this very address. In other words if this is the address of the object that you are interested it, value semantics will not do — even if you are not operating on addresses explicitly.

Since we mention addresses, this is the right place to mention pointers here. They are very peculiar, because they are objects that store values. However, the values they store are addresses in memory which can hardly be used for anything else than referring to objects. Therefore at the higher level of abstraction passing and returning pointers to and from functions is usually considered passing by reference (reference semantics), even though technically you are passing values. But note that there is a limit to the analogy between pass-by-pointer and pass-by-reference, as nicely outlined in this article.

Next, the reason people often resort to allocating objects in free store (on the heap) and returning a (more or less) smart pointer to it, is that value semantics does not play well with OO techniques that require access to objects via references or pointers to enable virtual function calls. It is also not easy to pass polymorphic types by value, because we are risking slicing. There are techniques to overcome this difficulty, like type erasure, but they require some non-trivial effort to implement. They are used in std::function, std::shared_ptr (for storing allocator). There are libraries that assist with implementing type erasure like, well, type_erasure or Adobe Poly Library.

One other common reason for choosing not to use value semantics is the fear for performance hit. Is this fear justified? The very short answer is: sometimes it is, sometimes it isn’t and sometimes it is avoiding value semantics that hits your performance.

Performance of value semantics

C++ encourages value semantics. The language is designed to make value semantics as useful and as fast as possible. Passing by value may be faster than you think. Let’s consider this example:

vector<string> getPoems()
{
  vector<string> ans;
  // fill ans;
  return ans;
}

vector<string> poems = getPoems();

How many times does the copy constructor of vector<string> get called? The answer is: not even once! This is because we do not require the two copies of the value — we only want to hand the value over. This is where the move constructor is chosen. Move constructor for a typical vector implementation costs six pointer assignments regardless of the types being stored or the vector’s size. So, let’s rephrase the question: how many times the move constructors gets called? In efficient compiler with all global optimizations enabled: most probably, not even once! This is owing to the compiler optimization technique known as copy elision.

Now, how about passing the arguments by value? First, let’s consider the situation where in the function implementation you need to make the copy of the argument. It is often the case when we implement the copy assignment and want to provide strong (commit-or-rollback) guarantee. In such cases passing by value, and not making the local copy is more efficient in general and equally efficient as passing by reference in the worst case. This is very well described by Dave Abrahams in this article.

In the other case, where only want to query the given value for some attributes, passing by reference is more efficient that passing by value for types with expensive copy constructors.

int remainingRoom( Dictionary const& d ) 
{
  return d.maxRoom() - d.size(); 
}

void fun()
{
  Dictionary d = buildDictionary(); // cheap by-value
  int remaining = remainingRoom(d); 
  use(d);
}

Here, if we passed the dictionary by value to function remainingRoom we would cause an unnecessary copying. While passing the arguments by a reference to const object eliminates the unnecessary copy, it should be kept in mind that it is passing by reference, and it does not mean that the object referred by d will not change. I refer you again to the very important article by Andrew Koenig. So, could a similar copy elision not be applied in this case? C++ as it is specified today does not allow that, but there does not appears to be anything that shoul make such an optimization hard. It has been called ‘compile-time copy-on-write’ by Dave Abrahams. It is likely that we will find this optimization in C++ in the future.

Passing by value is not copying

While we were able to see that sometimes passing by value requires the call to copy constructor, in general this is not so. Have a look at this example.

unique_ptr<int> make1()
{
    return unique_ptr<int>( new int(1) );
}

unique_ptr<int> increment( unique_ptr<int> i )
{
    ++*i;
    return i;
}

int main()
{
    unique_ptr<int> p = increment(make1());
}

Notice that function increment takes the argument and returns by value. Yet std::unique_ptr does not have a copy constructor. Do you find it surprising? It is not surprising if you adapt the C++ way of thinking: you tell the compiler that you are interested in values rather than objects, and the compiler chooses the best tool to implement your intentions. It has a couple of tools:

  1. Copy elision
  2. Move constructor
  3. Copy constructor
  4. The ‘as-if’ rule: it enables the compiler to do whatever it wants as long as you cannot tell the difference.

More…

Value semantics are a way of thinking about the program and computations. It cannot be comprehensively described in a short post. I just wanted to provide an overview of the idea. There is a lot more to be learned. Just to give you a couple of references:

  1. C++Next has a nice series of articles on value semantics.
  2. N2479 — this proposal tries to formally define the value.
  3. “Fundamentals of Generic Programming” provides a definition of a Regular Type, which is also a core of value semantics.
About these ads
This entry was posted in programming and tagged , . Bookmark the permalink.

10 Responses to Value semantics

  1. Gunnar Hansen says:

    A particularly bad explanation of value semantics. You contrast with “objects” implying that value semantics is not OO. You imply a disparity where there is none. value sematics is of course just as OO, and should be contrasted with reference semantics. In C++, A = B means that the value of B was assigned to A. In languages like C# and Java, A = B means that the reference A now points to whatever B was pointing to. Very different.

    In C++, you can instantiate instances (objects) of a Class (user defined type) with the normal value semantics of the built in types. For example,

    float a;
    MyClass b;

    We can also declare a pointer reference if we want:

    MyClass* c = new MyClass();

    In C#, we can only declare a reference:

    MyClass c = new MyClass();

    So, C# and Java have reduced functionality compared to C++, and are certainly no more object oriented.

    • Hi Gunnar.
      Thank you for taking the time to read my article and to contribute. I am still trying to understand what value semantics are, even though have been using it for years, and I may have got something wrong. However I do not believe that our views differ that much.

      I do compare “value semantics” to “OO” but my goal is not to say they are exact opposites — what I want is to show the difference. And I believe the difference exists, it looks like (tell me if I am wrong) you and I think of something different when we say “OO”. I wonder what “OO” means for you. I can tell you what it means for me. It is definitely more than just being able to define user-defined types. If I write:

      class Rational
      {
      int numerator_;
      int denumerator_:

      public:
      // copy, assignment, comparison, stream operators
      int numerator() const { return numerator_; }
      int denumerator() const { return denumerator_; }
      };

      I do not call it OO just yet. It is only the ability to create a user-defined type that provides a convenience and a certain degree of encapsulation (according to my understanding of the term). What I understand as “object oriented” is the ability to add run-time polymorphism (based on virtual function table); and this feature in order to work requires of the programmers to pass references or pointers to functions and return pointers. And in this sense it is in conflict with value semantics. I may have failed to highlight what I understand under term “OO” enough. I will try to add this to the post. In particular, under my understanding of the term, “OO” has nothing to do with using objects (as defined in C++).

      But let me note that just comparing two things does not imply that they are opposite things. It only implies that there exist some differences. I agree with you that “value semantics” could be contrasted with “reference semantics” and I believe I said this in this post. I chose the way of describing “value semantics” by showing what it is not. And I do not think it is particularly bad an approach.

      Next, the difference in assignment you show between Java and C++ is of course true, but I believe saying only this would be to little to show the nature of value and reference semantics. I only put a short link to this article by Scott Stanchfield, but I believe it is very enlightening. I also used to think that passing by pointer (not by reference!) as in Java is no different than passing by reference. But now I can see it is different, and Scott explains it very well.

      I am not well familiar with C#, so you can correct me if I am wrong, but I believe it does provide optional value semantics: when you define your type with keyword class you request reference semantics, whereas keyword struct requests value semantics.

      • Gunnar Hansen says:

        Andrzej,

        The problem with your post is that you don’t seem to understand what OO is all about. In short, it is combination of encapsulation, inheritance and polymorphism. All of these 3 are completely present in C++. Value semantics is a feature of the language that is orthogonal to the OO nature of the language. It does nothing to diminish it. For example,

        MyCircle myRoundThing; // The MyCircle class is derived from Circle, derived from Shape, INHERITING methods and data.
        myRoundThing.radius( 20 ); // The MyCircle class ENCAPSULATES the data and operations appropriate to MyCircle
        myRoundThing.draw(); // Virtual function draw, which behaves POLYMORPHCIALLY, since it calls the Circle::draw()

        All 3 of the essential characteristics of an OO language are demonstrated using value semantics. Polymorphism means that the behavior changes based on the type.

        >> under my understanding of the term, “OO” has nothing to do with using objects (as defined in C++).

        As I’ve explained, OO certainly is all about Object Orientation, using objects which are instances of user defined types that feature encapsulation, inheritance and polymorphism.

        >> But let me note that just comparing two things does not imply that they are opposite things. It only implies that there exist some differences

        I never said they were opposite, but neither is there any difference, because the two concepts are orthogonal. In other words, they have nothing to do with each other.

  2. Hi Gunnar,
    I believe that in your example, you got the feature of “polymorphism” wrong. Let me quote it here.

    MyCircle myRoundThing;      // The MyCircle class is derived from Circle,
                                // derived from Shape, INHERITING methods and data.
    myRoundThing.radius( 20 );  // The MyCircle class ENCAPSULATES the data
                                // and operations appropriate to MyCircle
    myRoundThing.draw();        //  Virtual function draw, which behaves 
                                // POLYMORPHCIALLY, since it calls the Circle::draw()
    

    Your example shows that you can inherit member functions (methods) from base classes and encapsulate data, but it does not show any OO-style run-time polymorphism. Calling member functions inherited from base classes is not polymorphism. In your example, you do not even need to declare Circle::draw() a virtual function, and you will achieve the same effect. Polymorphism is something somewhat the opposite: when you can call method defined in MyCircle by only being aware of the interface: Shape.

    When you only use two thirds of OO, (inheritance and encapsulation) but do not use polymorphism, then you are right, such use cases get along very well with value semantics; and they are often useful. But if you also start using polymorphism (and OO is much about polymorphism) you will soon discover that it is hard, if possible, to use it without passing the arguments by references or by pointers.

    Examples from Wikipedia probably explain OO-style polymorphism better than I.

    • Gunnar Hansen says:

      >> but it does not show any OO-style run-time polymorphism. Calling member functions inherited from base classes is not polymorphism.

      You still don’t understand. Polymorphism comes from the fact that each object has a hidden piece of data which points to the Virtual Table that is created for each class. This is the key to polymorphic behavior. It’s there regardless of whether you access the object with value semantics, reference semantics or pointer semantics. That’s why I say that value semantics is completely orthogonal to polymorphism and therefore, OO.

      For example, I could create a MyCircle object and add it to a list of Shape objects. Then, I could loop through the list and call:

      ShapeList[c].draw();

      These objects will behave polymorphically, which means that the method actually executed will depend on the type of object that it is. All with value semantics.

      >> In your example, you do not even need to declare Circle::draw() a virtual function, and you will achieve the same effect.

      You missed the point. The fact that I did declare draw a virtual function means that I’m invoking the polymorphism feature of C++. That means that a VTable reference is embedded in the object at construction time. This means that polymorphic behavior is built into the object, and not at all dependent on value semantics.

      • Hi Gunnar, it looks we are getting somewhere. I am trying to identify at which point we really disagree (because we seem to agree on many things). It looks like you claim that “value semantics is completely orthogonal to polymorphism” and I claim that there is a tension between value semantics and run-time polymorphism (based on virtual table look-up). Agreed?

        Now, let me go through your post:

        Polymorphism comes from the fact that each object has a hidden piece of data which points to the Virtual Table that is created for each class. This is the key to polymorphic behavior.

        I agree so far, although “this is the key” seems a bit imprecise: having a virtual table pointer is a way of implementing run-time polymorphism but it is not a polymorphism per se. C++ does not require that compilers provide virtual tables if they can implement the same polymorphic behaviour by other means. (but in practise it is always virtual tables, I guess).

        It’s there regardless of whether you access the object with value semantics, reference semantics or pointer semantics.

        If by “it” you mean “a hidden piece of data which points to the Virtual Table” than, this I agree that we would find “it” in a newly created object, or in an object referred to by a named reference, or in an object pointed to by a pointer.

        That’s why I say that value semantics is completely orthogonal to polymorphism and therefore, OO.

        This is where I disagree with you. Only having “a hidden piece of data which points to the Virtual Table” does not mean using polymorphism. In fact, you have shown a good example the last time how one can create a polymorphic type and then choose not to use this polymorphism. Of course the example was very scarce, so I can only guess how you defined your types (sorry if I guessed wrong) but here is how I imagine you define your classes:

        struct Shape {
          virtual void draw() = 0;
          // other stuff
        };
        
        struct Circle : Shape {
          void draw() /*override*/ {...}
          virtual void radius(int i) {...}
          // other stuff
        };
        
        struct MyCircle : Circle {
          void draw() /*override*/ {...}
          void radius(int i) /*override*/ {...}
          // other stuff
        };
        
        // later on:
        MyCircle myRoundThing;
        myRoundThing.radius( 20 );  // no virtual table look-up
        myRoundThing.draw();        // no virtual table look-up
        

        In this example (the three last lines), since the compiler knows the exact type of the object (MyCircle) already it will not bother to look the functions up in the virtual table to call the two functions. Even though “a hidden piece of data which points to the Virtual Table” is there. Compiler does not need it, because it can already see it hs an object of type MyCircle and it will call its methods directly. Really. Check it for yourself. Remove every keyword “virtual” and every “=0″ and the effect of the last three lines will be exactly the same: the right methods from MyCircle will be chosen. No “hidden piece of data which points to the Virtual Table” is required. This is because even though you created a polymorphic type Shape (and Circle) you decided not to use them, and abandoned the polymorphism. The example that would show how virtual table is engaged is this:

        void drawAll( Shape & s1, Shape & s2 ) {
          s1.draw();
          s2.draw();
        }
        

        Here, the compiler does not know what is the type of the object referred to by s1 or s2 so in order to execute the correct draw it has to look the virtual table up. But note that I used references. And I could not use values:

        void drawAll( Shape s1, Shape s2 ) {
          s1.draw();
          s2.draw();
        }
        

        This wouldn’t even compile. But even if it would, it would pick the wrong function. Do you agree?

        For example, I could create a MyCircle object and add it to a list of Shape objects. Then, I could loop through the list and call:

        ShapeList[c].draw();

        These objects will behave polymorphically, which means that the method actually executed will depend on the type of object that it is. All with value semantics.

        Your example is too short to prove any point. Can you tell us what the type of ShapeList is? How do you insert elements of type MyCircle to this list? How do you store them in the list? I can only guess. I guess that you are not using a built-in C++ array, becuse I could not imagine how you would add elements to it. On the other hand if you are using some STL container or a data structure written by yourself what does operator[] return? A reference or a value? I am pretty sure it is a reference.

        The fact that I did declare draw a virtual function means that I’m invoking the polymorphism feature of C++.

        No. It only means you declare a polymorphic type. But whether this potential polymorphism is really used or not depends on how you use your types.

        That means that a VTable reference is embedded in the object at construction time.

        Correct.

        This means that polymorphic behavior is built into the object, …

        No, it only means that you can use polymorphism if you choose so later.

        … and not at all dependent on value semantics.

        You choose to use polymorphism if you operate on pointers or references to a base type. You choose not to use polymorphism if you operate on values (even though “a VTable reference is embedded in the object at construction time”)

        • Gunnar Hansen says:

          Ok, that wasn’t a good example. Here is a better one:

          Shape ShapeList[3];
          Shape myShape;
          Square mySquare;
          Circle myCircle;
          ShapeList[0] = myShape;
          ShapeList[1] = mySquare;
          ShapeList[2] = myCircle;

          for each( auto s in ShapeList )
          {
          s.draw();
          }

          A) result: shapeshapeshape
          ————————————————————–
          Shape* ShapeList[3];
          Shape myShape;
          Square mySquare;
          Circle myCircle;
          ShapeList[0] = &myShape;
          ShapeList[1] = &mySquare;
          ShapeList[2] = &myCircle;

          for each( auto s in ShapeList )
          {
          (*s).draw();
          }

          B) result: shapeSquareCircle
          ———————————————————————
          Shape ShapeList[3];
          Shape myShape;
          Square mySquare;
          Circle myCircle;
          ShapeList[0] = myShape;
          ShapeList[1] = mySquare;
          ShapeList[2] = myCircle;

          for each( auto s in ShapeList )
          {
          (&s)->draw();
          }

          C) result: shapeshapeshape

          In (A), the result shows that the polymorphism didn’t work, and it might look to you like value semantics was the cause.

          in (B), the draw function is called with value semantics. A couple of definitions of the dereference operator:

          “* is the dereference operator and can be read as “value pointed by.”
          “The dereference operator * takes a pointer to a value (variable or object) and returns the value.”

          The result in (B) shows that polymorphism did work, and you might think that using a pointer and dereferencing it is the key.

          However, in (C), we call the function with pointer semantics. Polymorphism doesn’t work. The reason is that semantics has nothing to do with polymorphism. The difference is caused by the fact that in (A) and (C), the array is of type Shape. That defines the type of the object, because the size must be set at compile time. In C++, base class member variables are stored first, then derived after that. When I added an object of type Circle to an array of Shapes, it necessarily truncated the circleness, and stored it as a Shape. Later, whether I access that object with value semantics or pointer semantics, it’s still only a Shape. In (B), the array type is Shape*, and since a pointer is a fixed size, no truncation takes place.

          That’s why it’s called “semantics”. It refers to the common cultural meaning:

          Semantics is commonly used to refer to a trivial point or distinction that revolves around mere words rather than significant issues: “To argue whether the medication killed the patient or contributed to her death is to argue over semantics.”

          So, if your point is that one has to be careful to avoid truncating or casting away the type of an object, then you’re right. Passing by value is one easy way to do that. My point is that how an object is accessed semantically does not affect polymorphism.

  3. David says:

    Hi Andrzej,
    I liked you blog, I invite you to check it out mine; about templates.
    surprising-code.blogspot.com

    Good work,
    David

  4. Hi Gunnar,
    I guess I sort of agree with your summary:

    So, if your point is that one has to be careful to avoid truncating or casting away the type of an object, then you’re right. Passing by value is one easy way to do that. My point is that how an object is accessed semantically does not affect polymorphism.

    But let me say it with me say it with my own words. I say that “value semantics” means passing arguments to functions and returning values from functions by value. (You could disagree with my definition). I say that doing so (returning by value) looses not only the type of the object but also object’s identity itself. After the return it is a new and different object (because it has a different address) even though it stores the same value.

    This contrasts with OO programming, where you create objects, but you pass them around by interfaces. You never really want a reference to the most derived object, but a reference to the interface the object implements. This is how I understand the core of OO.

    And this is why I think that value semantics (passing by value) is not suitable for OO, because you cannot pass interfaces by value. If you used my implementation of Shape (I enclosed it before; member function draw is declared as pure virtual function) such passing the interfaces by value is even syntactically incorrect (and your examples wouldn't compile either). So I guess in your implementation Shape is not an interface, and you are not using the full power of OO. But that's fine. You don't have to.

    I interpret your example a bit differently. In example A, you slice the two objects by using assignment operator. Thus, you store three objects of type Shape. It is not that the polymorphism "did not work". By choosing to slice, you simply stated that you are not interested in object mySquare anymore and you only want to extract it generic Shape data into another object. Sometimes it is useful.

    In example B, by assigning the address of mySquare to the stored pointer, you said that you are interested in the very object: not any copy of it. You simply want to have two ways of referring to the object: one is simply mySquare, the other is *ShapeList[1]. Even though pointers are passed by value (as in Java), you are effectively using almost reference semantics.

    In example C, as in example A, you slice the two objects. You state that you are not interested in object mySquare anymore and you only want to extract it generic Shape data into another object. The reason that ShapeList[1] does not refer to the same object as name mySquare, is that by choosing to store objects of type Shape you chose to use something like value semantics for type Shape; not the fact that you are using some pointers in the call.

    There is one other thing that I disagree with:

    “* is the dereference operator and can be read as “value pointed by.”

    “The dereference operator * takes a pointer to a value (variable or object) and returns the value.”

    The indirection (or dereference) operator semantics are more clearly expressed by "object pointed by". The operator does not return a value: it returns a reference to an object. This is why you can assign to the result:

    int i = 0;
    int* p = &i; // p points to object i
    *p = 2; // assigns 2 to object i
    

    This wouldn't have worked if indirection operator didn't return a reference to an object.

    But having said all that, I agree with your statement (if I understand it correctly) that if you choose to use values of the most derived object (like Square), the same member functions would be called, no matter if you use the object name or a reference or a pointer:

    Square mySquare;
    Square & myRef = mySquare;
    Square * myPtr = &mySquare;
    
    mySquare.draw(); // same fun
    myRef.draw(); // same fun
    myPtr.draw(); // same fun
    

    But let me point this again, passing the values (or even pointer or references) of the most derived type around rather that passing the interfaces is far from OO, as I understand it.

  5. Gunnar Hansen says:

    >> I say that “value semantics” means passing arguments to functions and returning values from functions by value. (You could disagree with my definition)

    Yes, I disagree with your definition. “Value semantics” is referring to how the operators work, like the assignment operator (=), plus operator, etc. It means that objects work just like the primitive types. I got this crazy idea from Bjarne Stroustup’s definitive book on C++. That was his goal in designing C++.

    Float a=3;
    Float b=2;
    b = a;
    a=8;
    With reference semantics, b now has a value of 8. With value semantics, b has a value of 3.

    “With reference semantics, assignment is a pointer-copy (i.e., a reference). Value (or “copy”) semantics mean assignment copies the value, not just the pointer. C++ gives you the choice: use the assignment operator to copy the value (copy/value semantics), or use a pointer-copy to copy a pointer (reference semantics). C++ allows you to override the assignment operator to do anything your heart desires, however the default (and most common) choice is to copy the value.”

    Well, it’s been fun sparring with you, but I’ve made my point, and if we continued, I’d just be repeating myself.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s