CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 19
  1. #1
    Join Date
    Jan 2004
    Location
    Düsseldorf, Germany
    Posts
    2,401

    Unhappy Using References as member variables

    Hi,

    I am just in the process of changing some classes in my program to have references instead of pointers as member variables. One idea behind it is that it should not be able to initialize any member of the class with a null pointer.

    However I came at some point to realize that references can be as dangerous as pointers. Below is some code which dumps core on the last line, for obvious reasons.

    Code:
    #include <string>
    #include <iostream>
    
    class T
    {
    public:
            T( std::string& s )
               : _s( s ) {};
            void print() { std::cout << _s << std::endl; }
    private:
            std::string& _s;
    };
    
    int main()
    {
            T * myobj = 0;
            try {
               // Creating a string on the stack
               std::string stackstring( "Hello World!" );
               myobj = new T( stackstring );
               myobj->print();
               // Checking if myobj really contains a reference
               stackstring = "Hello Mom!";
               myobj->print();
            }
            catch ( ... ) {
               delete myobj;
               throw;
            }
            if ( myobj ) {
               // myobj still exists but stackstring not
               myobj->print();
            }
    }
    Now does anybody know of some magic tricks to avoid these kind of problems? I can only think of making _s a pointer again and checking if it is 0 each time before accessing it.

    Thanks for any thoughts on this subject.

  2. #2
    Join Date
    Nov 2003
    Posts
    1,405
    You want to avoid leaks in the event of exceptions right? Why not have a look at class auto_ptr of the standard library.

  3. #3
    Join Date
    May 2000
    Location
    Washington DC, USA
    Posts
    715
    seems like your using your refference like a variable. Why not just make it a variable and not a pointer or a refference.

    As for checking a pointer to make sure it's not zero every time you use it, that's why you should use access methods. Then you only check it once and you've got protection throughout....

    If you've got your heart set on a refference you could of course define a member variable and pass a refference in your access method....

  4. #4
    Join Date
    May 1999
    Location
    Southern California
    Posts
    12,266
    As far as I know, it is not common to store a reference or pointer in a general-purpose class. I think it more common to store a copy of an object. If you want to store a reference, you could add code to validate the object before using it. So in the print member function, you could somehow validate _s.

    I don't know what function that would be; it would, however, be implentation-dependent, right? It would help if the standard had a funtion we could call that would determine if an address is a valid address.
    "Signature":
    My web site is Simple Samples.
    C# Corner Editor

  5. #5
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    There's a simple rule to follow. Never reference from an outer scope a variable in an inner scope. This covers your problem as well as the dreaded "return reference to local" newbie error.

    The scope of your T object is the main function (because you delete it at the end, you could emphasize this by using std::auto_ptr or boost::scoped_ptr instead of a raw pointer). The scope of stackstring is the try block. By assigning this object to the reference in the T object, which is in a "more outer" scope, you're set up for disaster.
    All the buzzt
    CornedBee

  6. #6
    Join Date
    Feb 2003
    Posts
    377
    I actually like using references as members instead of pointers when a null value is not allowed. Your example does not show a problem specific reference member variables, the same problem would occur if you used pointers:
    Code:
    #include <string>
    #include <iostream>
    
    class T
    {
    public:
            T( std::string* s )
               : _s( s ) {};
            void print() { if (_s) std::cout << *_s << std::endl; }
    private:
            std::string* _s;
    };
    
    int main()
    {
            T * myobj = 0;
            try {
               // Creating a string on the stack
               std::string stackstring( "Hello World!" );
               myobj = new T( &stackstring );
               myobj->print();
               // Checking if myobj really contains a reference
               stackstring = "Hello Mom!";
               myobj->print();
            }
            catch ( ... ) {
               delete myobj;
               throw;
            }
            if ( myobj ) {
               // myobj still exists but stackstring not
               myobj->print();
            }
    }
    As others have said, the problem is assigning a reference or pointer to variable in an inner scope and then using it in an outer scope. Using a pointer member and checking for null would not fix it.

  7. #7
    Join Date
    Jan 2004
    Location
    Düsseldorf, Germany
    Posts
    2,401
    Thanks everybody for your ideas. Sometimes it really makes me wonder how different worlds of IT can be. If you look at relational databases, referential integrity is a very important and a very easy to implement thing. You have a master table (which in my example would hold objects of type std::string) and you have a slave table (holding objects of type T). Now if an object of type T references an object in the first table either:
    - it is impossible to delete the referenced object
    - deleting the referenced object will also delete all objects referencing it (on delete cascade)

    The first option is imho impossible to implement in C++, the second one requires a lot of manual work (even if one uses smart pointers I assume it will take a lot of work).

    To jlou: thanks for pointing out that the problem exists with pointers as well. However, passing the address of an object created on the stack to a constructor (myobj = new T( &stackstring )) is something that an advanced programmer would most likely not do without checking exactly what's going on in the constructor. Passing a reference to a temp object to a constructor (myobj = new T( stackstring )) is a lot less suspicious, so the effect that it does not work will come as a nasty surprise. Which raises again the original question, if it is a good idea to use references as members...

  8. #8
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    Relational databases are a lot more specialized than a general-purpose programming language...
    All the buzzt
    CornedBee

  9. #9
    Join Date
    May 1999
    Location
    Southern California
    Posts
    12,266
    Originally posted by treuss
    The first option is imho impossible to implement in C++, the second one requires a lot of manual work (even if one uses smart pointers I assume it will take a lot of work).
    Probably they are easy if you want them to be.
    "Signature":
    My web site is Simple Samples.
    C# Corner Editor

  10. #10
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    It wouldn't be all that hard. You'd just have to do the work that the DB developers do.

    The real problem is the lack of backreferencing. What happens in your DB example if there are three tables, A, B and C? A and B both have foreign key fields that reference C. If something from A is deleted, it should delete the referenced value in C, but what about values in B that reference the same value in C? Do they get deleted too? And if the relationship is complex enough, wouldn't that lead to the possibility of emptying the entire database?
    All the buzzt
    CornedBee

  11. #11
    Join Date
    May 1999
    Location
    Southern California
    Posts
    12,266
    Originally posted by CornedBee
    It wouldn't be all that hard. You'd just have to do the work that the DB developers do.
    However not all that they do.
    Originally posted by CornedBee
    The real problem is the lack of backreferencing.
    In C and C++ there are things such as double-linked lists that make "backreferencing" easy.
    "Signature":
    My web site is Simple Samples.
    C# Corner Editor

  12. #12
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    That's not what I mean.
    All the buzzt
    CornedBee

  13. #13
    Join Date
    May 1999
    Location
    Southern California
    Posts
    12,266
    Then what do you mean?
    "Signature":
    My web site is Simple Samples.
    C# Corner Editor

  14. #14
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    I mean that an object doesn't know about a reference to it.
    All the buzzt
    CornedBee

  15. #15
    Join Date
    May 1999
    Location
    Southern California
    Posts
    12,266
    And how is that different from a database? How does the database equivalent of an object know about the database equivalent of a reference? I think the answer is that the database knows about objects and references, but database "objects" don't know about "references" to them.
    "Signature":
    My web site is Simple Samples.
    C# Corner Editor

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