Which is "safer"

This is a discussion on Which is "safer" within the C++ Programming forums, part of the General Programming Boards category; There are two commonly used methods of using the constructor to initalize data members, so I wondering, purley to improve ...

  1. #1
    Its hard... But im here swgh's Avatar
    Join Date
    Apr 2005
    Location
    England
    Posts
    1,479

    Which is "safer"

    There are two commonly used methods of using the constructor
    to initalize data members, so I wondering, purley to improve
    my program security, which is safer out of these two methods.

    method one:

    Code:
    class Foo
    {
    public:
       Foo();
    
    private:
       int data1;
       int data2;
       int data3;
    };
    
    Foo::Foo() :
       data1(0),
       data2(10),
       data3(100)
       {}
    method two

    Code:
    class Foo
    {
    public:
        Foo ( int, int, int );
    
    private:
       int data1;
       int data2;
       int data3;
    };
    
    // assume the argument values are passed from main
    Foo::Foo ( int d1, int d2, int d3  )
    {
       data1 = d1;
       data2 = d2;
       data3 = d3;
    }
    Thinking again, is calling a set member function on each data
    member to initalize more safer? As it would be possible to do this:

    Code:
    void Foo::setData1 ( int  d1 )
    {
       if ( d1 < 0 )
       {
          std::cout << "\nPassed value is negative!\n";
       }
    
       else
       {
          data1 = d1;
       }
    }
    I know the above code is not correct in general, but that is the general idea.

    I am wondering, which method is considered the most safe for initalizeing data
    members?

    Any help greatly appreciated!
    I'm just trying to be a better person - My Name Is Earl

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,915
    On above the other is not safer per se, it's more about efficiency. Using the initializer list calls the constructor.
    If you don't, then the default constructor will be called and the operator =.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Thinking again, is calling a set member function on each data
    member to initalize more safer?
    I don't think this is particularly safe. User code should not be responsible for putting an object into a usable state (you may forget to call some setters, you may call them in an incorrect order) and in your example there is a possibility that the variable is left uninitialized (if input is negative).

    There is also no reason why you can't check the arguments of the constructor and use a default value or throw an exception if the condition is not met. (If you have a good validating setter, you might use it in the constructor as well.)

    It seems even possible to use the throw statement in ternary operator to use in initializer list:
    Code:
    class X
    {
        int n;
        public:
        X(int x):
            n(x < 0 ? throw std::range_error("Input must not be negative") : x)
        {}
        //...
    };
    In any case, if your object is in a good usable state (all members initialized etc) after the constructor finishes that is "safe", and if that is not true (user needs to do something else to make the object usable, e.g call setters to "initialize" members) that is "unsafe".
    Last edited by anon; 07-16-2008 at 07:54 AM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  4. #4
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    Quote Originally Posted by anon View Post
    Code:
    class X
    {
        int n;
        public:
        X(int x):
            n(x < 0 ? throw std::range_error("Input must not be negative") : x)
        {}
        //...
    };
    I dont think this will compile, both expressions in the ternary operator must yield the same type

  5. #5
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The standard draft says this:
    --The second or the third operand (but not both) is a throw-expression
    (_except.throw_); the result is of the type of the other and is an
    rvalue.

    --Both the second and the third operands have type void; the result is
    of type void and is an rvalue. [Note: this includes the case where
    both operands are throw-expressions. ]
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  6. #6
    Registered User
    Join Date
    May 2008
    Location
    Paris
    Posts
    248
    swgh, you might want to be "const-correct":
    Code:
        Foo ( int const&, int const&, int const&);
    instead of
    Code:
        Foo ( int, int, int );
    This first uses a reference, so it does not copy the argument, and second it is const-correct: it makes the compiler help you spotting some unwanted changes in the argument (which didn't occur for the copy, however).

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    7,344
    Why would you want to use references here? It adds overhead without adding anything of value. If you want to protect against changing the int (which usually isn't necessary) then make the parameters const int instead of int or const int&.

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    MarkZWEERS: passing by const reference is only really beneficial for objects that are bigger than integer - this particularly applies to std::string, std::vector, struct/class with more than a couple of (simple) member variables and other "potentially huge" objects.

    However, passing a simple integer actually ADDS overhead in that it passes the address of the integer, and for the relevant code to get to the actual value it will first have to fetch the address, then fetch the number.

    I'd personally say that anywhere "const int &" occurs is probably a good place to remove the & - having const there would indicate to anyone else that the value is just taken as is, and not changed, which can help the compiler in it's task of optimizing under some circumstance, but otherwise serves little purpose [and good compilers probably figures out that it's not changing anyways].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21