CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 28
  1. #1
    Join Date
    Jul 2002
    Posts
    788

    passing smart pointer

    I just want to confirm if this is a good way;

    In the below example, there is no need to pass smart pointer to Test class, as there is no need for Test object to manage any record. There can be hundreds of Test object created, but all its records are given a pointer created outside the class. The Test object can be deleted anytime anywhere though, but this doesn't make any difference.

    Code:
    class Test
    {
    
    Test(Record * rec)
    {
     _record = rec;
    }
    .....
    
    private:
    
    Record * _record;
    
    };
    
    main()
    {
      QSharedPointer<Record> myrecord(new Record());
      QSharedPointer<Test> mytest(new Test(myrecord->data()));
      
     
    
    
    }

  2. #2
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: passing smart pointer

    Is it possible that the Record object might be destroyed yet a Test object holding a pointer to it might still exist?
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  3. #3
    Join Date
    Jul 2013
    Posts
    576

    Re: passing smart pointer

    Quote Originally Posted by mce View Post
    I just want to confirm if this is a good way;
    It's not a good idea.

    Never use both pointers and smart pointers on an object type. It completely defeats the purpose of using a smart pointer in the first place. In fact it makes object deletion even messier. So it's either or (apart from very special situations).

  4. #4
    Join Date
    May 2007
    Location
    Scotland
    Posts
    1,164

    Re: passing smart pointer

    Quote Originally Posted by mce View Post
    I just want to confirm if this is a good way;

    In the below example, there is no need to pass smart pointer to Test class, as there is no need for Test object to manage any record. There can be hundreds of Test object created, but all its records are given a pointer created outside the class. The Test object can be deleted anytime anywhere though, but this doesn't make any difference.

    Code:
    class Test
    {
    
    Test(Record * rec)
    {
     _record = rec;
    }
    .....
    
    private:
    
    Record * _record;
    
    };
    
    main()
    {
      QSharedPointer<Record> myrecord(new Record());
      QSharedPointer<Test> mytest(new Test(myrecord->data()));
      
     
    
    
    }
    There are a few issues with your code.

    1) Do not use leading underscores, the C++ Standard states:
    17.4.3.1.2 Global names [lib.global.names]
    1 Certain sets of names and function signatures are always reserved to the implementation:
    — Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter
    (2.11) is reserved to the implementation for any use.
    Each name that begins with an underscore is reserved to the implementation for use as a name in the global
    namespace.
    That basically means if you define variables with leading underscores you are running the risk of trouble.

    2) Avoid using dynamic memory allocation except when it is really necessary.

    3) A class should take a copy of any data passed to it by a native pointer to avoid the questions "Who owns this data?", "Can I delete it?" and in the case of pointers to memory of POD type, "How do I delete it (free, delete or delete[] ???)".

    4) If a pointer is a pointer to an array, then the size of that data should accompany the pointer.

    5) If a pointer on a function interface is to a single object the the pointer should be marked const so that it cannot be incremented. In fact, single bits of data should in general only be passed to a class by value or by reference, the only case that I can think of when this is not true is when the data is optional, in which case a const pointer pointer should be specified on the function interface with a default null value (e.g. the function signature should follow the form void f(T* const optionalPieceOfData = 0); )

    6) If you do dynamically allocated memory, prefer RAII to smart pointers.

  5. #5
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: passing smart pointer

    Quote Originally Posted by PredicateNormative
    1) Do not use leading underscores (...) That basically means if you define variables with leading underscores you are running the risk of trouble.
    In practice, this is sound advice, but I note that in this case the name is in the namespace of the class, so it does not actually violate the rule, though it poses an unnecessary risk.
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  6. #6
    Join Date
    May 2007
    Location
    Scotland
    Posts
    1,164

    Re: passing smart pointer

    Quote Originally Posted by laserlight View Post
    In practice, this is sound advice, but I note that in this case the name is in the namespace of the class, so it does not actually violate the rule, though it poses an unnecessary risk.
    Agreed.

  7. #7
    Join Date
    Jul 2013
    Posts
    576

    Re: passing smart pointer

    Quote Originally Posted by PredicateNormative View Post
    There are a few issues with your code.
    And there are even more issues with your advice.

    2) Avoid using dynamic memory allocation except when it is really necessary.

    Use dynamic memory whenever appropriate (design, object lifetime, personal style, etcetera). In OO for example dynamic allocation of all polymorphic objects is a common strategy.

    3) A class should take a copy of any data passed to it by a native pointer to avoid the questions ...

    Do not apply defensive copying routinely (what if the data is huge for example). Access rights and ownership issues should be handled by other means.

    4) If a pointer is a pointer to an array, then the size of that data should accompany the pointer.

    Pass what you need. For example there is usually no need to pass the size of a C-string.

    5) In fact, single bits of data should in general only be passed to a class by value or by reference,

    There are no other parameter passing mechanisms available (than by value and by reference)

    6) If you do dynamically allocated memory, prefer RAII to smart pointers.

    Smart pointers are the quintessential example of RAII usage.
    Last edited by razzle; August 15th, 2014 at 08:14 AM.

  8. #8
    Join Date
    May 2007
    Location
    Scotland
    Posts
    1,164

    Re: passing smart pointer

    Quote Originally Posted by razzle View Post
    And there are even more issues with your advice.
    Ok, let's go through these one by one. I'll deal with point 5 first, because it is really important to the context of other things that I'll write in this post.

    Quote Originally Posted by razzle View Post
    Quote Originally Posted by PredicateNormative
    5) In fact, single bits of data should in general only be passed to a class by value or by reference,
    There are no other parameter passing mechanisms available (than by value and by reference)
    In C++ there are three mechanisms for passing data, by value:
    Code:
    void f(T value);
    void f(const T value);
    by reference:
    Code:
    void f(T& value);
    void f(const T& value);
    and by pointer:
    Code:
    void f(T* value);
    void f(const T* value);
    void f(T* const value);
    void f(const T* const value);
    where T is any valid type. There is a very good reason why I have stated that "In fact, single bits of data should in general only be passed to a class by value or by reference". Here is the explanation.

    Lets say we have a single object of type T that we want to pass through an interface that we will define - what are our options?

    By value:
    Code:
    A) void f(T value);
    B) void f(const T value);
    A) OK, we have a copy of the T object that was passed to the function f. So, the following holds true:
    i) We don't need to do null checks since we have a copy of the actual data and therefore, it is safe to access the object - this is good for efficiency.
    ii) For larger T objects the copying of the object on the interface will be less efficient than passing by reference or by pointer - this is bad for efficiency.
    iii) If T is a base class, then the derived part of the object will be sliced - we will only have a copy of the base part of the object - this is very bad.
    iv) If the design of T is good, then any changes that are made to value are not reflected in the calling object.
    v) The object is an automatic object so we do not need to worry about cleaning any memory up and that is good.
    B) Same as A), but we have the added protection against the programmer making the mistake of accidentally assigning to the copy when they didn't intend to do so - this kind of thing sometimes happens when someone goes to check equivalence but accidentally leaves out one of the equals symbols (i.e. if(value = some_other_value) instead of if(value == some_other_value) );

    By reference:
    Code:
    C) void f(T& value);
    D) void f(const T& value);
    C) OK, we have a reference to the T object that was passed to the function f (i.e value is an alias of the actual object passed to the function). So, the following holds true:
    i) We don't need to do null checks since it is not possible to legally create a null reference, therefore we can legitimately assume that it is safe to access the object - this is good for efficiency.
    ii) For larger T objects passing the object by reference will be more efficient than passing by value, and the same performance as passing by pointer - this is good for efficiency.
    iii) If T is a base class, then that is fine, we have an alias to the base part of the derived object - the object will not be sliced, but instead we will have the restricted base interface - all is good.
    iv) Any changes to value will be changes to the object passed to f(T&) in the calling code - this may be the desired behaviour, if not use D)
    v) The reference is an automatic object, and since it is not legitimate to delete the object pointed to by the reference (e.g. delete &value) we do not need to worry about cleaning any memory up and that is good.
    D) Same as C), but changes to value and hence the object passed to f(const T&) are not allowed.

    By pointer:
    Code:
    E) void f(T* value);
    F) void f(const T* value);
    G) void f(T* const value);
    H) void f(const T* const value);
    E) So, we have been passed a pointer to some piece of memory. Therefore, the following holds true:
    i) The value could be null, so a null check needs to be made - this is bad for efficiency and adds extra decision making to the code. What happens if the pointer is null? Generally the function may be able to do nothing, but is the valid for the object? Maybe it is, maybe it isn't, that will need to be assessed on a case by case basis. If it isn't acceptable (i.e. the class requires the data to be valid), then how will the problem of a null pointer be reported by the class user? Return value? That will require extra code, at the very least, a list of potential return values and their meaning. What about an exception? That may require extra exception handling code into the caller - plus because the caller is passing data to a function by pointer, the caller will need to anticipate that an exception could be thrown (since in our case the return is void and therefore (in the absence of documentation, which is a likely scenario and without spending the time to look through the code of f) it is a reasonable thing to assume that the f(T*) might throw if the data is null, thus, a safe programmer might under certain circumstances feel the need to add in exception handling code in their calling function - though it is a rare occurance, I've seen this happen in the past. Either way, if value is passed by reference, these are decisions that need to be made that would not even be on the agenda with pass by value or pass by reference.
    ii) For larger T objects passing the object by reference will be more efficient than passing by value, and the same performance as passing by pointer - this is good for efficiency.
    iii) If T is a base class, then that is fine, we have an pointer to the base part of the derived object - the object will not be sliced, but instead we will have the restricted base interface - all is good.
    iv) Any changes to value will be changes to the object passed to f(T*) in the calling code - this may be the desired behaviour, if not use F or H)
    v) Who owns the memory? I've seen people write code that allocates memory in a global function, passes the the return pointer to multiple dynamically allocated objects and then has the fun of figuring out who should be responsible to deleting the memory. They weren't able to use smart pointers because the code was multi-threaded - I'll explain why not to use smart pointers in multi-threaded code further down. There were so many dynamic allocations going on the that project where memory wasn't owned by any particular object that memory was getting leaked all over the place - the only reasonable solution was to scrap the code and start again with a proper safe design. When I asked the developer who wrote the original code, "why did you write it that way", his answer was - "because it's my style"! In the case of function f, is the writer of f responsible for deleting the memory? Probably not, but I can almost guarantee that if someone writing multi-threaded code allocates too many peices of memory that are not managed by objects, then some maintainer somewhere down the line trying to fix leaks will attempt to free memory in a place where they think is reasonable given the task they have and that might be in function f. Clearly this would be bad.
    vi) For all the function writer knows, the memory could be to a single object, or to an array of data... can the pointer be incremented? Maybe, but the function writer generally cannot guarantee whether or not that is true, or by how much it can be incremented without an extra value being passed with the pointer that states the number of elements of data. Therefore, unless the data is of type char*, the only reasonable thing to do is assume that it cannot be incremented.
    F) Same as E), except that the data pointed to cannot be modified
    G) Same as E), except that the pointer cannot be modified (incremented, decremented or reassigned)
    H) Same as G), except that the data pointed to cannot be modified

    If you take a step back and look at the above evidence, you will see that the sensible thing is to only pass single objects by value or by reference, not by pointer unless the data is optional - a way of specifying this is to provide a default null value on the function interface. i.e.
    Code:
    void f(T* const value = 0);
    void f(const T* const value = 0);

    Quote Originally Posted by razzle View Post
    Quote Originally Posted by PredicateNormative
    2) Avoid using dynamic memory allocation except when it is really necessary.
    Use dynamic memory whenever appropriate (design, object lifetime, personal style, etcetera). In OO for example dynamic allocation of all polymorphic objects is a common strategy.
    I can tell you have never had to work on real-time embedded systems or safety related systems. You would need to learn a whole other way of working. But that aside, even in general terms, dynamic memory allocation should be avoided unless you have good reason to use it (e.g. for large allocations). I'll put in reasons against each of your points:

    Design -
    a) An application design that uses dynamic memory allocations left right and centre is not efficient (calls through to the system for dynamic memory are expensive) and have other side effects that need to be accounted for. For example, if expressions new or new[] fail, an exception will be thrown.
    b) If smart pointers are not used, then the result of a) can be a memory leak (e.g. in the case of a copy assignment operator that is not exception safe, or in the case where many objects are being allocated in sequence, the programmer may fail (and I often see it in code reviews) to cover the possibility that a dynamic memory allocation of objects may fail and therefore they fail to write in the code to clean up the memory). On an embedded system this can be a big problem, because there may not be a nice OS to clean up memory after the process ends and memory leaks may waste valuable resources.
    c) If smart pointers are used, programmers tend to make use of the reference counting mechanism, to pass the same object round to many places, including other types of object. However, this prevents the application from being scalable as far as threading is concerned. The argument here is that in order to make access to the dynamically allocated object thread safe in cases where that object has been passed to different types of object, both the smart pointer and the object to which the smart pointer points need to be locked. This is a some what difficult problem, because i) the object attempting to do the lock does not "own" the object that needs to be locked (strictly speaking the object is owned by the smart pointer) and therefore, because the smart pointer has been shared between objects of different type it is not possible to get a guaranteed lock on the object to which the smart pointer is pointing. Even if you wrap the object into a lockable container so that you can lock the object you still have thread safety issues with the smart pointer itself. Basically, unless the smart pointer has been written in a thread safe manner (I've not yet seen a successful implementation that is thread safe), it is not possible to lock the reference counting object inside the smart pointer (this is generally another dynamically allocated object and therefore putting a lock around the smart pointer does not lock its associated reference counting object), so the smart pointer itself is therefore not thread safe. If your application suddenly needs to take advantage of multi-threading, you could end up with a re-write of significant parts.
    d) pointers need to be checked for null to prevent attempts to access a null pointer. This means that programmers must remember to make null checks when it is necessary for them to be done, they also need to know the system well enough to know when it is necessary and when it is not necessary to do a null check. Null checks require extra code, code which the programmer my forget to write, if they do remember, then they are adding in extra code control flow through the code that would need to be unit tested on a safety related system - code and tests that would not need to be there if the value was passed by reference (not by pointer).
    e) If smart pointers are not used then some object needs to be assigned to manage the resource, but this is not a bad thing - it means that the object is scalable to ever changeing requirements. Basically, it is not a bad idea to adopt a design that can be easily modified for a multi-threaded environment.
    f) A design that allows dynamic memory to be allocated on one side of a shared object boundary (i.e. DLL) and deallocated on the side of the boundary is also bad - the behaviour is undefined. Hence, I don't know if you have done this or not, but make sure that you never use a smart pointer on a shared object interface.

    Object Liftime -
    a) This should be managed through the application design, objects that are required to be dynamically allocated in order for their lifetime to be increased suggests bad design. Smart pointers aside, objects should be destroyed at the same level at which they have been created. This way their state and intended scope is easily trackable.
    b) This should never be used as an argument for using dynamic allocation! Dynamic memory allocation opens up a whole world of potential programming errors (thus increases the possiblity of unsafe or easily breakable code). Thus the safe way of writing code is to avoid unnecessarily dynamically allocating objects (i.e. aoid using dynamic memory allocation except when it is really necessary).

    Polymorephic objects -
    Dynamic memory allocation is not necessary to make use of polymorphic objects. On occasion, a design may require dynamic memory allocation, but such allocations should be handled by a factory object that manages the allocation and deallocation of the object (the factory object may optionally provide a function to request the deallocation an object, but it should always clear up any allocations it has made when it goes out of scope).



    Quote Originally Posted by razzle View Post
    Quote Originally Posted by PredicateNormative
    3) A class should take a copy of any data passed to it by a native pointer to avoid the questions ...
    Do not apply defensive copying routinely (what if the data is huge for example). Access rights and ownership issues should be handled by other means.
    With all due respect, I don't know where you have been obtaining your advice, but I would suggest finding another source. Safety should always come before efficiency - you are accruing technical debt and voiding the safety and security of your application by writing code that is not safe or robust. Decreasing defensive copying should never be done at the expense of code safety and security. In the case where data is passed by native pointer, the only safe thing to do is for the object to which it has been passed to copy the data. Following this principle, the object owns the copy; it knows how the memory was allocated; it knows how to correctly deallocate the data; it can handle safe copying and deletion of the data when the object itself is copy constructed, assigned or destroyed; the class for the object can be modified for thread safety without any change necessitated to any other part of the code; it can manage the lifetime of the data it holds. If you have a piece of huge data, or, you want to decrease defensive copying without decreasing safety and security, then don't pass it by pointer and don't copy it - encapsulate the allocation in an RAII object and pass the object by reference - no copying required and the data can be safely/securely read and manipulated by the member function.


    Quote Originally Posted by razzle View Post
    Quote Originally Posted by PredicateNormative
    4) If a pointer is a pointer to an array, then the size of that data should accompany the pointer.
    Pass what you need. For example there is usually no need to pass the size of a C-string.
    If a pointer is a pointer to an array, then the size of that data should accompany the pointer, this is because in general, there is no way in telling from the pointer or first element of data where the data ends, therefore a size for the array needs to be provided. A C-String is a special case - the data itself should contain a null termination value. However, since it cannot be guaranteed that the person that generated the char array has remembered to put a null value at the end of the C-String, it is entirely possible that a function reading the data in a char array could over-run the end of the array, at which point all bets are off and undefined behaviour sets in. For this reason, compiler venders (e.g. Microsoft), explicitly warn about using C++ Standard compliant functions such as strcpy, stating that their own safe vendor functions should be used instead (these functions have parameters that specify the size of the data to be copied). Indeed, many safety standards explicitly disallow the use of any array pointer that is not accompanied by its size variable. So I stand by my statement, that the size of the array should accompany the pointer.


    Quote Originally Posted by razzle View Post
    Quote Originally Posted by PredicateNormative
    6) If you do dynamically allocated memory, prefer RAII to smart pointers.
    If you use smart pointers you are applying RAII.
    Although technically what you have stated is true; I intended you would realise what I was inferencing in my statement - that lack of clarity is my fault. That is, that an RAII object that manages all aspects of the resource (including aquisition) should be preferred to a smart pointer. Scalability for thread safety is the obvious reason.

    In short, prefer automatic objects to dynamically allocated object, prefer pass by reference to pass by pointer. Prefer RAII objects (that manage all aspects of the resource management) to smart pointers.
    Last edited by PredicateNormative; August 15th, 2014 at 08:36 AM. Reason: spelling

  9. #9
    Join Date
    Jul 2013
    Posts
    576

    Re: passing smart pointer

    Quote Originally Posted by PredicateNormative View Post
    5) In C++ there are three mechanisms for passing data,
    No, there are just two parameter passing mechanisms, by value and by reference. (See for example The C++ Programming Language by Stroustrup).

    To pass a pointer is not a parameter passing mechanism. It's like passing any type and you do it either by value or by reference. The term "pass by pointer" is a misnomer and should be avoided even though it will be understood.

    The two basic mechanisms can be qualified (const/non-const, rvalue/lvalue reference, etcetera) and of course there are reasons to use one over the other depending on the situation, but no one is generally preferred.

    2) I can tell you have never had to work on real-time embedded systems or safety related systems. You would need to learn a whole other way of working. But that aside, even in general terms, dynamic memory allocation should be avoided unless you have good reason to use it
    In systems with hard real-time constraints there are two options, either prohibit dynamic memory allocations altogether or use an allocator with timing guarantees. So not even in the special case of real-time should dynamic allocations obviously be avoided, and certainly not in the general case.

    Many, maybe even most modern programming languages rely heavily on dynamic memory. If you're into object orientation and functional programming and want to do it well, dynamic memory is essential. In C++ there are ways to accomplish this in a safe, efficient and orderly manner. Your objections are moot and typical of those who view C++ through a C lens.

    Full use of object orientation and functional programming requires dynamic memory and in my view C++ is as suitable for this as say Java and C#. Your advice implies that C++ doesn't play in the same league. I beg to differ. Okay, C++ may require a little more on part of the programmer but that's a small price to pay for the extra flexibility.

    3) With all due respect, I don't know where you have been obtaining your advice, but I would suggest finding another source.
    I rely on the object oriented and functional approaches to secure and robust code. Routinely applied defensive copying has no place here. In C style C++ it may look different but even so you wouldn't want extensive copying to turn your program into a dinosaur in a tar pit.

    4) So I stand by my statement, that the size of the array should accompany the pointer.
    If you cannot trust programmers to terminate a C string how can you trust them to pass the right string size? If the size is needed then pass it but it won't incrase safety. A much better advice is to graduate from C arrays and start using safer C++ alternatives such as STL containers.

    6) That is, that an RAII object that manages all aspects of the resource (including aquisition) should be preferred to a smart pointer. Scalability for thread safety is the obvious reason.
    Thread safety is not guaranteed by the RAII idiom. For that additional precaution is needed. In the smart pointer case the internal counter must be atomic. Furthermore the variable that's holding the smart pointer may have to be atomic depending on the situation, just like when an ordinary pointer is used.

    So smart pointers are RAII and thread safety concerns are no reason to avoid them. On the contrary. It's better to apply a well known established abstraction systematically rather than inventing the RAII wheel over and over again on every occasion. Prefer smart pointers whenever applicable.
    Last edited by razzle; August 18th, 2014 at 03:18 AM.

  10. #10
    Join Date
    May 2007
    Location
    Scotland
    Posts
    1,164

    Re: passing smart pointer

    Razzle, my apologies for not replying sooner, although I read your post on Saturday, due to a number of commitments, this is the first opportunity I have had to respond. I'm a bit limited for time at the moment, but let's see how I get on - anything I fail to respond to today, I'll respond to tomorrow.

    Quote Originally Posted by razzle View Post
    No, there are just two parameter passing mechanisms, by value and by reference. (See for example The C++ Programming Language by Stroustrup).

    To pass a pointer is not a parameter passing mechanism. It's like passing any type and you do it either by value or by reference. The term "pass by pointer" is a misnomer and should be avoided even though it will be understood.
    Ok, take a step back - we're talking about different things (though you might not think so at the moment). Semantically, in C++ an object of type T can be passed by reference or by value, they are the only two options. However, the thing that you seem to be missing is that although for pass by value semantics you are restricted to the mechanism of passing by value type, for pass by reference semantics, you have the option of one of two mechanisms, either passing by reference type or passing by pointer type.

    Going back to basics, in simple terms, what is a reference? It is simply something that refers to something else (both passing by pointer type and passing by reference type come under the semantic category of passing by reference). In other words, pointer types and reference types have reference semantics. Thus for pass by value (semantic), in practical terms you pass by value (type); but for pass by reference (semantic), you have the option of pass by reference (type) or pass by pointer (type).

    If you are still not convinced. Consider the following code:

    Code:
    #include <iostream>
    #include <vector>
    
    class test
    {
    public:
      test(const std::vector<int>& v)
        :vec(v)
      {}
    
      const std::vector<int>& vec;
    
    private:
      test& operator=(const test);
    };
    
    void f(const std::vector<int>& v)
    {
      std::cout << v.size() << std::endl;
    }
    
    void f(const std::vector<int>* const v)
    {
      if(v != 0)
      {
        std::cout << v->size() << std::endl;
      }
    }
    
    int main()
    {
      std::vector<int> vec;
    
      test t(vec);
    
      f(vec);      //1) Pass vec by reference semantics
      f(t.vec);    //2) What is happening here? Is vec being passed by reference again or is t.vec be passed by value?
      f(&vec);     //3) Pass by reference semantics - specified as pass by pointer
      f(&(t.vec)); //4) Pass by reference semantics - specified as pass by pointer
    
    }
    In comment 2), I pose the question, is vec being passed by reference again or is the t.vec const reference object being passed by value? The answer is that both are true... well kind of. Strictly speaking, in "pass by" terminology, it is the object that you are intending to pass through the interface that is the subject matter. In this case, the vector is the subject matter, hence in both 1) and 2) vec is passed by reference semantics even though in 2) the const reference type is copied by value.

    Similarly, in case 3), it is the vector that is the subject matter, semantically vec is passed by reference, even though the temporary pointer is copied by value (in practice though, the compiler will optimise out the copy). In case 4, vec is passed by reference semantics, yet the address of vec stored by the reference type is copied to a temporary pointer type that is then copied by value (again, these copies will be optimised out by the compiler).

    The point is though, in all 4 cases the vector is always the subject matter and in every case pass by reference semantics are utilised. In short, pass by reference (type) and pass by pointer (type) are both forms of pass by reference (semantic); hence the term "pass by pointer" is not a misnomer, but a specific case of pass by reference (semantic). If you are still really not convinced by what I am saying, then read the following sections of the FAQ-Lite, they'll no doubt explain reference semantics vs value semantics better than I just have.

    http://www.parashift.com/c++-faq/val...semantics.html
    http://www.parashift.com/c++-faq/ref...imes-good.html
    http://www.parashift.com/c++-faq/pass-by-value.html

    Most C++ books tend to deal with the most simple case of pass by reference semantics, that of passing a single object, hence they utilise the reference type to demonstrate the advantages of pass by reference semantics. Passing by native pointer, in general is not appropriate for passing a single object, so you shouldn't expect to find an example of how it should be done in any C++ book worth it's salt. There are numerous pieces of C++ literature around that explain why passing a single object by pointer should not be done - the points of which I have briefly summarised in my previous post - see Item 12 of More Effective C++ for a particular example (to do with catching exceptions).

    Hence, I stand by my comment that "In C++ there are three mechanisms for passing data", one for pass by value semantics (pass by value type) and two for pass by reference semantics (pass by reference (type) and pass by pointer (type)).

    Anyway, I've run out of time, so I'll need to reply to the rest tomorrow. As a final note though, your objections would seem to suggest that you have taken it that I have been saying that you should never use dynamic memory allocation, that is certainly not the case, I have simply said that it should be avoided if not really necessary - automatic objects being preferred when appropriate. There are plenty of examples of where dynamic allocation is "necessary", for example, in cases where a large amount of memory is required, or the case where the bounds of the amount of memory is not known at compile time (container classes are a good example), in certain OO design patterns (e.g. creational patterns), etc.
    Last edited by PredicateNormative; August 19th, 2014 at 02:25 AM. Reason: Added etc.

  11. #11
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: passing smart pointer

    pass by reference (type) and pass by pointer (type) are both forms of pass by reference (semantic); hence the term "pass by pointer" is not a misnomer, but a specific case of pass by reference (semantic).
    I agree with the comment made by Razzle in post #9. In c++ there are only two parameter passing mechanisms; pass by value and pass by reference - irrespective of the type of the argument. Pointers, classes, containers etc etc can be passed either by value or by reference. When passing by value the appropriate copy constructor (or default copy constructor) for the type of argument is used.
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  12. #12
    Join Date
    May 2007
    Location
    Scotland
    Posts
    1,164

    Re: passing smart pointer

    Quote Originally Posted by 2kaud View Post
    I agree with the comment made by Razzle in post #9.
    Having spent all the time that I was going to spend replying to the rest of razzles last post, researching the subject of reference semantics, I'm changing my mind - I don't agree with the argument that passing a pointer is not pass by reference just because it is copied on the interface (i.e. passed by value), since although the actual pointer is passed by value, a reference object can be passed by value too, but that does not make it any less a reference - the difference is, it matters that the pointer has been copied, since the copy cannot be considered the same as the original, the reference copy on the otherhand can. Passing a pointer is not pass by reference because of what a pointer is, it is an object with rights (the pointer itself is addressable, accessable, modifiable and therefore it becomes the subject matter), a reference has none of those rights (and is therefore never the subject matter). It doesn't take much of a search on Google to realise that there is a split in the community over whether or not passing a pointer is pass by reference semantics or pass by value semantics. At the end of the day, it all comes down to what you believe the term "reference semantics" means and whether or not the type that you are considering conforms to that meaning. It seems there are two schools of thought.

    1) Reference semantics covers objects whose only responsibility is to refer to another object of the specified type (i.e. references and pointers), allowing indirect access and thus avoiding copying of the actual object. Basically, the argument is that these objects are forms of reference because their only job is to refer to another object, or
    2) Reference semantics means that the reference acts in absolute terms as an alias to the actual object in every respect (giving up any ability for the reference object itself to be addressed, accessed or manipulated, hence any action made to the reference actually takes place on the referenced object). This view only allows C++ reference types.

    Marshall Cline clearly follows the former view. Nicolai Josuttis, states in the context of the STL (chapter 6, section 6.11.2 of his book The C++ Standard Library - 2nd edition):

    Unfortunately, there is no support for reference semantics in the C++ standard library. However, you can implement reference semantics in terms of value semantics. The obvious approach to implementing reference semantics is to use pointers as elements
    It would seem that he recognises that pointers do not meet the strict rules of reference semantics (first sentence), but sort of allows pointers to be under the banner of reference semantics in this case (only because a stricter option is not available). It might be worth me pointing out that his statement "there is no support for reference semantics in the C++ standard library" is not saying that C++ does not support reference semantics. The statement is in the context of the STL, and is probably alluding to the fact that containers of reference types are strictly forbidden and thus unsupported.

    The thing is though, the second view of reference semantics that I mention as far as I can tell is more inline with the definition of reference semantics with respect to Computer Science (and other languages) - I couldn't find any official concrete specification of the term "reference semantics", which is a shame, but reading through many, many web pages, it seems that the second view that I have written is a reasonable representation of the position that many hold. Following this second view, I don't think the argument that "pointers are not pass by reference because the pointer itself is actually passed by value" is the real argument as to why pointers are not really pass by reference. I think it is more down to what a pointer type is and what a reference type is. At the end of the day, a pointer is an object that has the attributes that it is itself addressable, accessable, modifiable (reassignable), a reference has none of those attributes and therefore does not really have the concept of a normal object. It would seem that the reference type in C++ has been designed to follow the second view of reference semantics. Therefore, regardless of whether or not a reference object is copied by value on the interface, a reference is still a reference, hence it still conforms with the second view of reference semantics - the same cannot be said for pointers.

  13. #13
    Join Date
    Jul 2013
    Posts
    576

    Re: passing smart pointer

    Quote Originally Posted by PredicateNormative View Post
    Having spent all the time ...
    We're dealing with an established terminology used for parameter passing to functions. The basic distinction is between pass by value and pass by reference. Only those. There's is no by pointer, no by array, no by address, no by air or what have you. This terminology is upheld both by Stroustrup in The C++ Programming Language and by the C++ standard, and that's a pretty strong reason not to insist on conflicting usage.

    The purpose of the established terminology is to make a distinction strictly limited to the actual parameter passing. The focus is on the parameter itself and not on what it represents in a wider context. So if the parameter is a pointer (or smart pointer for that matter) you're not passing what it points to, you're passing the pointer and you can do that either by value or by reference as you can any parameter.
    Last edited by razzle; August 21st, 2014 at 12:03 AM.

  14. #14
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: passing smart pointer

    Quote Originally Posted by PredicateNormative View Post
    At the end of the day, it all comes down to what you believe the term "reference semantics" means and whether or not the type that you are considering conforms to that meaning.
    Meh. Personally, I don't believe you could say either way based off of type alone, but rather "what" you consider the argument is. As you say, a pointer is itself both a value, and a reference to something else. A function may want to operate on either the pointer itself, or the pointed object.

    EG:

    Code:
    void modifyT(T* p); //reference semantic on T type
    void samePoints(T* p1, T* p2); //value semantics on T* type
    void findObject(T** p); //Reference semantics on T* type
    If you don't define what your functions are trying to do, and what the argument is, then it makes no sense to talk of reference/value semantics based off type alone. IMO, doing so is just a sterile back and forth argument.

    No context. No semantics.
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  15. #15
    Join Date
    Jul 2013
    Posts
    576

    Re: passing smart pointer

    Quote Originally Posted by monarch_dodra View Post
    No context. No semantics.
    You are confusing matters. There is always a context in the semantics of parameter passing, namely the actual passing. In all your examples value semantics is used. All pointers (parameters) are passed by value. In all cases could reference semantics have been used instead. Any pointer (parameter) could have been passed by reference if so desired.

    Again, the semantics of parameter passing pertains to the passing of the parameters, not their usage in a wider context. When value semantics is used it's called pass by value. When reference semantics is used it's called pass by reference.
    Last edited by razzle; August 21st, 2014 at 01:01 AM.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured