Thread: Dealing With Objects as Members of a Class

  1. #1
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513

    Dealing With Objects as Members of a Class

    I'm working my way through a C++ book (again), and am up to a section on composition.

    The book (Deitel) strongly recommends keeping such objects private: "[making member objects public] does violate the encapsulation and hiding of the containing class's implementation, so member objects of class types should still be private, like all other data members."

    When making a fun test program using this concept, making six classes that will be utilized by a "top" class, I noticed the number of functions in that top class needed to access all the features of those six objects blew up really quickly. (See example below).

    So my questions are:
    1) Is the Deitel advice applicable to a self-contained program, or more for classes intended for distribution?
    2) If the latter, is it acceptable to make member objects public for easier access?
    3) Or are there other mechanisms that eliminate this overhead that I haven't gotten to yet?

    Example:
    Let's say we have three classes:

    Code:
    class ClassA
    {
    public:
       int getA1();
       int getA2();
       int getA3();
       void setA1( int );
       void setA2( int );
       void setA3( int );
    private:
       int a1;
       int a2;
       int a3;
    };
    
    class ClassB
    {
    public:
       int getB1();
       int getB2();
       int getB3();
       void setB1( int );
       void setB2( int );
       void setB3( int );
    private:
       int b1;
       int b2;
       int b3;
    };
    
    class ClassC
    {
    public:
       int getC1();
       int getC2();
       int getC3();
       void setC1( int );
       void setC2( int );
       void setC3( int );
    private:
       int c1;
       int c2;
       int c3;
    };
    If we make member objects of these private in TopLevel:

    Code:
    class TopLevel
    {
    public:
       /* constructor and member initializers */
    
       int getA1() { return a.getA1(); }
       int getA2() { return a.getA2(); }
       int getA3() { return a.getA3(); }
    
       int setA1( int a1 ) { a.setA1( a1 ); }
       int setA2( int a2 ) { a.setA2( a2 ); }
       int setA3( int a3 ) { a.setA3( a3 ); }
    
       int getB1() { return b.getB1(); }
       int getB2() { return b.getB2(); }
       int getB3() { return b.getB3(); }
    
       int setB1( int b1 ) { b.setB1( b1 ); }
       int setB2( int b2 ) { b.setB2( b2 ); }
       int setB3( int b3 ) { b.setB3( b3 ); }
    
       int getC1() { return c.getC1(); }
       int getC2() { return c.getC2(); }
       int getC3() { return c.getC3(); }
    
       int setC1( int c1 ) { c.setC1( c1 ); }
       int setC2( int c2 ) { c.setC2( c2 ); }
       int setC3( int c3 ) { c.setC3( c3 ); }
    
       /* additional TopLevel functions */
    
    private:
       ClassA a;
       ClassB b;
       ClassC c;
    };
    ...but if we make them public, it can be simplified to:

    Code:
    class TopLevel
    {
    public:
       /* constructor and member initializers */
    
       /* additional TopLevel functions */
    
       ClassA a;
       ClassB b;
       ClassC c;
    };

  2. #2
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    277
    Quote Originally Posted by Matticus View Post
    I'm working my way through a C++ book (again), and am up to a section on composition.

    The book (Deitel) strongly recommends keeping such objects private: "[making member objects public] does violate the encapsulation and hiding of the containing class's implementation, so member objects of class types should still be private, like all other data members."

    When making a fun test program using this concept, making six classes that will be utilized by a "top" class, I noticed the number of functions in that top class needed to access all the features of those six objects blew up really quickly. (See example below).

    So my questions are:
    1) Is the Deitel advice applicable to a self-contained program, or more for classes intended for distribution?
    2) If the latter, is it acceptable to make member objects public for easier access?
    3) Or are there other mechanisms that eliminate this overhead that I haven't gotten to yet?

    Example:
    Let's say we have three classes:

    Code:
    class ClassA
    {
    public:
       int getA1();
       int getA2();
       int getA3();
       void setA1( int );
       void setA2( int );
       void setA3( int );
    private:
       int a1;
       int a2;
       int a3;
    };
    
    class ClassB
    {
    public:
       int getB1();
       int getB2();
       int getB3();
       void setB1( int );
       void setB2( int );
       void setB3( int );
    private:
       int b1;
       int b2;
       int b3;
    };
    
    class ClassC
    {
    public:
       int getC1();
       int getC2();
       int getC3();
       void setC1( int );
       void setC2( int );
       void setC3( int );
    private:
       int c1;
       int c2;
       int c3;
    };
    If we make member objects of these private in TopLevel:

    Code:
    class TopLevel
    {
    public:
       /* constructor and member initializers */
    
       int getA1() { return a.getA1(); }
       int getA2() { return a.getA2(); }
       int getA3() { return a.getA3(); }
    
       int setA1( int a1 ) { a.setA1( a1 ); }
       int setA2( int a2 ) { a.setA2( a2 ); }
       int setA3( int a3 ) { a.setA3( a3 ); }
    
       int getB1() { return b.getB1(); }
       int getB2() { return b.getB2(); }
       int getB3() { return b.getB3(); }
    
       int setB1( int b1 ) { b.setB1( b1 ); }
       int setB2( int b2 ) { b.setB2( b2 ); }
       int setB3( int b3 ) { b.setB3( b3 ); }
    
       int getC1() { return c.getC1(); }
       int getC2() { return c.getC2(); }
       int getC3() { return c.getC3(); }
    
       int setC1( int c1 ) { c.setC1( c1 ); }
       int setC2( int c2 ) { c.setC2( c2 ); }
       int setC3( int c3 ) { c.setC3( c3 ); }
    
       /* additional TopLevel functions */
    
    private:
       ClassA a;
       ClassB b;
       ClassC c;
    };
    ...but if we make them public, it can be simplified to:

    Code:
    class TopLevel
    {
    public:
       /* constructor and member initializers */
    
       /* additional TopLevel functions */
    
       ClassA a;
       ClassB b;
       ClassC c;
    };
    A good rule of thumb is this: if altering the member data has no effect on any member functions, then it can be public. (For the sake of usability, an additional higher-level interface to such public data may still be desirable.)

    That being said, the whole point of using classes is to help organize things logically. For many tasks the same can (for the most part) be achieved with structs and functional programming, and that is just fine if it works out as a much simpler interface than the class-based approach. I tend to use a mix of the two. Whenever some kind of resource management is involved, a well-encapsulated classes which employs the constructor/destructor RAII paradigm. Otherwise I prefer the "C-style" interface.

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    The problem here is all your inner classes are nothing but bags of bits. They don't do anything useful besides store data.

    Take three separate implementations for a stack.
    A stack has a well defined interface, you push things, and you pop things.

    But how you choose to implement that functionality can vary.
    Code:
    // roll your own allocation and top of stack
    class Stack {
        public:
            void push(int item);
            int pop(void);
        private:
            int        *m_stack;
            int         m_top;
            int         m_size;
    };
    
    // inner class tracks storage, but you still need top
    class Stack {
        public:
            void push(int item);
            int pop(void);
        private:
            vector<int> m_stack;
            int         m_top;
    };
    
    // list does all the work for you
    class Stack {
        public:
            void push(int item);
            int pop(void);
        private:
            list<int>   m_stack;
    };
    The whole point of keeping all that stuff private is that as a class implementer, you're free to swap one with another.
    All the users of Stack are completely oblivious (and simply don't care) as to what happens on the inside.

    If you let users mess with private data by making it public, one of two bad things happens when you change the class.
    - all the users of your class suddenly find that the code doesn't compile.
    - even worse, it does compile, but runs in unpredictable ways because you changed the meaning of a variable without telling anybody.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 6
    Last Post: 08-12-2009, 04:46 AM
  2. Replies: 7
    Last Post: 07-31-2007, 09:27 AM
  3. Replies: 4
    Last Post: 10-16-2003, 11:26 AM
  4. Replies: 1
    Last Post: 12-11-2002, 10:31 PM
  5. Dealing with i/o streams in a class
    By ender in forum C++ Programming
    Replies: 3
    Last Post: 03-22-2002, 05:26 PM

Tags for this Thread