CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 14 of 14
  1. #1
    Join Date
    Mar 2003
    Location
    Germany, K-Town
    Posts
    578

    protected members in base class

    I have to roll up this topic again, sorry.
    In this thread, someone said it would be dubious practice to declare data members 'protected' in a base class.
    But what if I have an abstract base class which only describes how child classes should look? Consider a base class Vehicle. All vehicles have an attribute speed. I MUST make that attribute protected, or otherwise a car (which is a vehicle) has no speed. I know you came off with some code showing that it IS dangerous for specific data to make it protected, but when I WANT the data to be inherited (and altered at some point) by derivates, there should be nothing "dubious" bout that. That's what protected is for, isn't it?
    - Matthias

    "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup

  2. #2
    Join Date
    Mar 2004
    Location
    Israel
    Posts
    638
    Well, don't forget that interface is derived too.
    You can use a private member in your abstract class, and provide get/set/whatever virtual functions that access this member.
    Ofcoure, its up to you whether to provide the interface as public or protected.

    [edit] to be more accurate, when using polymorphism the virtual interface
    is inherited.
    Last edited by Guysl; July 21st, 2004 at 03:45 PM.
    **** **** **** **** **/**

  3. #3
    Join Date
    Mar 2003
    Location
    Germany, K-Town
    Posts
    578
    But 'speed' is also a property of the child classes. Why should only Vehicle have a certain speed attribute?
    - Matthias

    "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup

  4. #4
    Join Date
    Mar 2003
    Location
    Germany, K-Town
    Posts
    578
    Let me give an example:
    Code:
    class Vehicle
    {
    public:
       Vehicle( int sp ): speed( sp ) {}
       virtual ~Vehicle() {}
    
    protected:
       int speed;
    };
    
    class Car: public Vehicle
    {
    public:
       Car( int sp ): Base( sp )
       {
            if( speed > 200 )
            {
                cout << "Too fast!" << endl;
                speed = 50;
            }
       }
    };
    What's wrong with that? Speed is an attribute which all cars have. The example isn't chosen too well because speed should only be set once, but what if data members are supposed to be altered by the child classes? Is your suggestion to do that:
    Code:
    class Vehicle
    {
    public:
       Vehicle( int sp ): speed( sp ) {}
       virtual ~Vehicle() {}
    
       virtual int GetSpeed() { return speed; }
       virtual void SetSpeed( int s ) { speed = s; }
    private:
       int speed;
    };
    
    class Car: public Vehicle
    {
    public:
       Car( int sp ): Base( sp )
       {
            if( GetSpeed() > 200 )
            {
                cout << "Too fast!" << endl;
                SetSpeed( 50 );
            }
       }
    };
    Is that what you mean?
    Last edited by matthias_k; July 23rd, 2004 at 12:56 AM.
    - Matthias

    "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup

  5. #5
    Join Date
    Mar 2003
    Location
    Germany, K-Town
    Posts
    578
    forgot adding a 'virtual' to the getter/setter to allow dynamic binding.
    - Matthias

    "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup

  6. #6
    Join Date
    Apr 1999
    Posts
    27,449
    Quote Originally Posted by matthias_k
    Let me give an example:
    Code:
    class Vehicle
    {
    public:
       Vehicle( int sp ): speed( sp ) {}
       virtual ~Vehicle() {}
    
    protected:
       int speed;
    };
    
    class Car: public Vehicle
    {
    public:
       Car( int sp ): Base( sp )
       {
            if( speed > 200 )
            {
                cout << "Too fast!" << endl;
                speed = 50;
            }
       }
    };
    What's wrong with that?
    What if you decide to rename your "speed" variable to "nSpeed", or "m_nSpeed" or you do something else that calculates the speed? You've broken every child class that mentions anything about "speed", since the variable or method of calculation has changed.

    By having this as a function (SetSpeed()), you can change the implementation in the base class without affecting the derived classes whatsover.

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; July 23rd, 2004 at 01:32 AM.

  7. #7
    Join Date
    Feb 2004
    Location
    USA - Florida
    Posts
    729
    Also, how can you guarantee that the speed variable in your base class will not be corrupted by a derived class? Can you guarantee that whatever value a derived class sets the speed variable, your base class will still work correctly? What if someone decides to do this:
    Code:
    class Rocket : public Vehicle
    {
    public:
       Rocket()
       {   speed = -500;    }
    };
    Does it make sense for a vehicle to have a negative speed? How can your base class check for this if speed was declared protected?
    Last edited by cma; July 23rd, 2004 at 01:04 PM.
    Hungarian notation, reinterpreted? http://www.joelonsoftware.com/articles/Wrong.html

  8. #8
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    The advantages of set/get functions are:

    1) A getter can return either a copy or a const reference to prevent corruption.

    2) A setter can do sanity checking on a supplied value (and, thus, reduce code duplication).

    For example, suppose your vehicle class has a current speed and a max speed. Anything that wants to set the current speed has to check against the maximum speed. By putting in a setter function, you localise that check. And, of course, the cry "but the spec doesn't require that" turns out to be a magical incantation that guarantees that the spec will change in less than a New York second.

    3) You can treat derived values (i.e. values that do not correspond to a member, but are computed on-the-fly) the same way as direct values. Corollary: you can remove a data member if you find that it becomes redundant.

    4) (Related to 3) You are at liberty to alter the representation of the attribute without affecting anyone else.

    Don't get seduced by the argument "well, what's the difference?" Today, there may be no difference at all except for a bit more typing. Next month, when the client changes their mind yet again, it could make all the difference. It's about defensive programming: why throw away potential future advantage for the sake of a few keystrokes now.

    Don't forget, the protected section of your class is to derived classes what the public section is to the rest of the world - it's an interface that defines your class's capabilities. Treat the protected section the same way you treat the public section: it's just the target audience that's different.
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  9. #9
    Join Date
    Mar 2003
    Location
    Germany, K-Town
    Posts
    578
    Does it make sense for a vehicle to have a negative speed? How can your base class check for this if speed was declared protected?
    A setter can do sanity checking on a supplied value (and, thus, reduce code duplication).
    Hmm, if the setter is virtual (which it should be as someone said above), derivates can still override it and screw everything up. The base class still has no control whatsoever.
    - Matthias

    "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup

  10. #10
    Join Date
    Mar 2004
    Location
    Israel
    Posts
    638
    Hmm, if the setter is virtual (which it should be as someone said above), derivates can still override it and screw everything up. The base class still has no control whatsoever.
    Not really. The override virtual functions can access a base member using
    the interface tha base itself provide. If it provides a setter, you can't skip
    its checks and limitations.
    **** **** **** **** **/**

  11. #11
    Join Date
    Feb 2004
    Location
    USA - Florida
    Posts
    729
    Hmm, if the setter is virtual (which it should be as someone said above), derivates can still override it and screw everything up. The base class still has no control whatsoever.
    Then show an example of this using the second code in the post with the virtual accessor and mutator member functions.
    Hungarian notation, reinterpreted? http://www.joelonsoftware.com/articles/Wrong.html

  12. #12
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    Quote Originally Posted by matthias_k
    Hmm, if the setter is virtual (which it should be as someone said above), derivates can still override it and screw everything up. The base class still has no control whatsoever.
    In this case there is absolutely no need and every reason not to make the setter/getter virtual. The only time that the base class should define a virtual setter or getter is when the attribute is logically part of the public interface (or is needed by a base class function), but the base class cannot (for whatever reason) provide an implementation for that attribute. It's then up to each derived class to provide the implementation. Encapsulation maintained; polymorphism achieved,
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  13. #13
    Join Date
    Mar 2003
    Location
    Germany, K-Town
    Posts
    578
    Okay you've convinced me one more time
    - Matthias

    "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." - Bjarne Stroustrup

  14. #14
    Join Date
    Mar 2004
    Location
    Israel
    Posts
    638
    Originally posted by me:
    The override virtual functions can access a base member using
    the interface tha base itself provide. If it provides a setter, you can't skip
    its checks and limitations.
    That is what I meant:
    Code:
    class Base{
    public:
      Base(int val)  { defaultSetter(val); }       
      virtual void setter(int val) = 0;  
    
    protected:
      void defaultSetter(int val) 
      {
        m_Base =  (val>100 ? 100 : val);   
      }
    
    private:          
      int m_Base;
    };
    //=================================
    class Derived1 : public Base{
    public:
       Derived1(int val) : Base(val) {} 
       void setter(int val)
       {
           if(val < 10)
              defaultSetter(10);
           else 
           if(val > 80)
              defaultSetter(80);
           else
              defaultSetter(val);           
       }     
    };    
    //=================================
    class Derived2 : public Derived1{
    public:
       Derived2(int val) : Derived1(val) {}  
       void setter(int val)
       {
           if(val < 50)
              defaultSetter(50);
           else 
           if(val > 60)
              defaultSetter(60);
           else
              defaultSetter(val);           
       }     
    };
    Last edited by Guysl; July 24th, 2004 at 06:05 AM.
    **** **** **** **** **/**

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