Question on base class access spec. and constructor

In summary: DerivedClass.h fileclass DerivedClass : public BaseClass{public:DerivedClass(int x){x=0;}public int... y(){return x+y;}};In summary,The main point of private or protected inheritance is to restrict access to the base class portion of the derived class. If that is the case, it's back to square one. DerivedClass.h include BaseClass.h, then if outsider programs put #include "BaseClass.h", they can directly access to all the public members of BaseClass. Then what is the point of DerivedClass to declare class DerivedClass : private
  • #1
yungman
5,755
293
I studied about 20 pages, I understand the base class access specification as shown;
Base class spec.jpg

My question is why is it necessary to have private and protected base class access specification? If z is public in Base class, other programs can access z directly regardless the Derived class say it's private or protected. Is there really real use for declaring private or protected?Regarding to Constructors, is declaring constructor DerivedClass() : BaseClass() only to save some lines of declaring some default values of the variable and function. That if you are willing to type the extra lines on constructor DerivedClass(){}, you do not need to do this fancier way?

Thanks
 
Technology news on Phys.org
  • #2
I think this boils down to how the final API for a class will look to the user. For example I often use STL base classes. If these are left public then people are free to use the underlying container class API which pretty much keeps you from ever changing it without breaking everyones code. Keeping the underlying base class private allows you to make public only those parts you want people to use.

For small classes I'll often start by making them structs in development. In C++ structs are just the same as classes but everything is public by default. Once the API is clearer change to classes and then tweak public / protected specs as needed to make the API clean.
 
  • Like
Likes yungman
  • #3
Paul Colby said:
I think this boils down to how the final API for a class will look to the user. For example I often use STL base classes. If these are left public then people are free to use the underlying container class API which pretty much keeps you from ever changing it without breaking everyones code. Keeping the underlying base class private allows you to make public only those parts you want people to use.

For small classes I'll often start by making them structs in development. In C++ structs are just the same as classes but everything is public by default. Once the API is clearer change to classes and then tweak public / protected specs as needed to make the API clean.
Thanks for the reply, but like in examples of the book, the main() can see the base class, so whatever the derived class declare, other people can access to the public members of the base class object. Unless you can hide the base class object from the outside.

Can you give me a simple example how that matters?

Thanks
 
  • #4
I think I see how it works, if the main program only have #include "DerivedClass.h"only, then it will not see the BaseClass.h at all. So you can hide the BaseClass.h from outside users. So you can set the limit what outside users can access. Am I getting this right?

Thanks
 
  • #5
Well, just because the base class of your class is private doesn't remove the base class object from use by others, they just can't access the base classness of your class. So I should be able to define my own vector containers independent of your usage in your class.

There are cases when one and only one of a base class can be made. This happens in controlling hardware all the time. In cases like these one might well want to hide the constructor and not let anyone access it but you.
 
  • #6
yungman said:
I think I see how it works, if the main program only have #include "DerivedClass.h"only, then it will not see the BaseClass.h at all. So you can hide the BaseClass.h from outside users. So you can set the limit what outside users can access. Am I getting this right?
Unlikely. If DerivedClass inherits from BaseClass and DerivedClass is defined in DerivedClass.h and BaseClass is defined in BaseClass.h, then DerivedClass.h will have to include BaseClass.h, hence both definitions are visible to any file that includes DerivedClass.h.

The point of private or protected inheritance is not to hide information, but rather to restrict access to (i.e. restrict use of) the base class portion of the derived class.
 
  • #7
jbunniii said:
Unlikely. If DerivedClass inherits from BaseClass and DerivedClass is defined in DerivedClass.h and BaseClass is defined in BaseClass.h, then DerivedClass.h will have to include BaseClass.h, hence both definitions are visible to any file that includes DerivedClass.h.

The point of private or protected inheritance is not to hide information, but rather to restrict access to (i.e. restrict use of) the base class portion of the derived class.
If that is the case, it's back to square one. DerivedClass.h include BaseClass.h, then if outsider programs put #include "BaseClass.h", they can directly access to all the public members of BaseClass. Then what is the point of DerivedClass to declare class DerivedClass : private BaseClass{}? The outside program just go straight to BaseClass and use it's member functions and data, bypassing whatever restrictions DerivedClass put on! For example:
C++:
//BaseClass.h file
class BaseClass
{private int x;
public int y;
......
};//DerivedClass.h file
#include "BaseClass.h"
class DerivedClass : private BaseClass//declare private
{
....
}//Outside program
#include "BaseClass.h"// Just include BaseClass.h so I can access y in BaseClass.
#include "DerivedClass.h"
int main()
{
     y = 100;//Go directly to BaseClass and change y, who care what DerivedClass says?
}

Let's just assuming all 3 files are in separate files, main() just put #include "BaseClass.h" in line 18, totally bypasses the restriction in line 11. main() can access y anyway or any member function of BaseClass that is public!

So again, what is the point of all the fuzz? The whole thing is going in circle!

yungman said:
Regarding to Constructors, is declaring constructor DerivedClass() : BaseClass() only to save some lines of declaring some default values of the variable and function. That if you are willing to type the extra lines on constructor DerivedClass(){}, you do not need to do this fancier way?
Also, how about this part of my question on constructor?

Thanks for your time.
 
Last edited:
  • #8
yungman said:
Let's just assuming all 3 files are in separate files, main() just put #include "BaseClass.h" in line 18, totally bypasses the restriction in line 11. main() can access y anyway or any member function of BaseClass that is public!

So again, what is the point of all the fuzz? The whole thing is going in circle!
Did you try it? I guarantee that if you try to compile the following, you will get an error due to attempting to access the private member variable y of class DerivedClass:
C++:
class BaseClass
{
private:
    int x = 0;
public:
    int y = 0;
};

class DerivedClass : private BaseClass
{
};

int main()
{
    DerivedClass derivedObj;
    derivedObj.y = 100;
    return 0;
}
 
  • #9
yungman said:
Regarding to Constructors, is declaring constructor DerivedClass() : BaseClass() only to save some lines of declaring some default values of the variable and function. That if you are willing to type the extra lines on constructor DerivedClass(){}, you do not need to do this fancier way?
Can you paste an example of this? If you want DerivedClass to construct BaseClass using the no-argument constructor of BaseClass, it is not necessary to call BaseClass() explicitly. However, if the BaseClass constructor requires arguments, then you will have to call the BaseClass constructor in the definition of DerivedClass, like this:
C++:
#include <iostream>
#include <string>

class BaseClass
{
public:
    BaseClass(int myInt, const std::string& myStr) : n(myInt), s(myStr) {}
    int intVal() const { return n; }
    std::string strVal() const { return s; }
private:
    int n;
    std::string s;
};

class DerivedClass : public BaseClass
{
public:
    DerivedClass() : BaseClass(5, "hello") {}
};

int main()
{
    DerivedClass derivedObj;
    std::cout << derivedObj.intVal() << " " << derivedObj.strVal() << std::endl;
    return 0;
}
 
  • #10
jbunniii said:
Did you try it? I guarantee that if you try to compile the following, you will get an error due to attempting to access the private member variable y of class DerivedClass:
C++:
class BaseClass
{
private:
    int x = 0;
public:
    int y = 0;
};

class DerivedClass : private BaseClass
{
};

int main()
{
    DerivedClass derivedObj;
    derivedObj.y = 100;
    return 0;
}
No, I am saying I can access y from BaseClass directly, not through DerivedClass. Since I can see DerivedClass include BaseClass, all I have to do in my program ( main()) is to put #include "BaseClass.h" and I can bypass DerivedClass and go get y. I change your program:

C++:
class BaseClass
{
private:
    int x = 0;
public:
    int y = 0;
};

class DerivedClass : private BaseClass
{
};

int main()
{
    BaseClass BaseObj;//Go direct to BaseClass bypassing DerivedClass.
    BaseObj.y = 100;//I give y = 100.
    return 0;
}

The only way this will work is if BaseClass don't have any public member, use all protected member. But then, why even bother to declare any Base class access specification?If I understand correctly, if I know the name of BaseClass, I can write:
C++:
class Sneaky : public BaseClass//Backdoor to BaseClass
{
    setY(y){}
};
int main()
{
     Sneaky  Sneak
     Sneak.setY(100);//I get to y, doesn't matter what DerivedClass declared
}

Thanks
 
Last edited:
  • #11
yungman said:
No, I am saying I can access y from BaseClass directly, not through DerivedClass. Since I can see DerivedClass include BaseClass, all I have to do in my program ( main()) is to put #include "BaseClass.h" and I can bypass DerivedClass and go get y. I change your program:

C++:
class BaseClass
{
private:
    int x = 0;
public:
    int y = 0;
};

class DerivedClass : private BaseClass
{
};

int main()
{
    BaseClass BaseObj;//Go direct to BaseClass bypassing DerivedClass.
    BaseObj.y = 100;//I give y = 100.
    return 0;
}
Sure, but now DerivedClass isn't involved at all. Of course if you instantiate a BaseClass object, then the BaseClass's access permissions will be the ones that control what you can do with it.

This does not in any way change the fact that if you instantiate a DerivedClass, you won't be able to access its y member.

If I understand correctly, if I know the name of BaseClass, I can write:
C++:
class Sneaky : public BaseClass//Backdoor to BaseClass
{
    setY(y){}
};
int main()
{
     Sneaky  Sneak
     Sneak.setY(100);//I get to y, doesn't matter what DerivedClass declared
}
Sure, if you define a class that publicly derives from BaseClass, then you can access the public members of BaseClass. No one has claimed otherwise. What is sneaky about that?

The point of class DerivedClass : private BaseClass is not to prevent people from instantiating BaseClass directly, or from defining other classes like your Sneaky that are public BaseClass. The point is that users of DerivedClass won't be able to access the BaseClass portion of DerivedClass.
 
Last edited:
  • #12
Maybe an example will help. Suppose I have a simple class that prints strings to an output file.
C++:
class StringPrinter
{
public:
    StringPrinter(const std::string& fileName);
    void printString(const std::string& str);
private:
    ...
};
This class is perfectly usable on its own, and there's no reason you can't instantiate it directly and use it.

Now suppose I want to make a new class which will have the same behavior as StringPrinter but will automatically add a timestamp to every string that is printed.
C++:
class TimeStampedStringPrinter : private StringPrinter
{
public:
    TimeStampedStringPrinter(const std::string& fileName) : StringPrinter(fileName) {}
    void printTimeStampedString(const std::string& str)
    {
        std::string timeStamp = makeTimeStamp();
        std::string timeStampedStr = timeStamp + " " + str;
        // Use the base class's printString() to print the timestamped string.
        // We can do this because TimeStampedStringPrinter is derived from StringPrinter.
        printString(timeStampedStr);
    };
}
You would use TimeStampedStringPrinter as follows:
C++:
int main()
{
    TimeStampedStringPrinter printer("output.txt");
    // example output:
    // 10:37:15 Hello!
    printer.printTimeStampedString("Hello!");
    // can't call the base class's printString():
    printer.printString("trying to print without timestamp"); // this will cause a compilation error!
    return 0;
}
Note that the user of TimeStampedStringPrinter cannot call TimeStampedStringPrinter::printString() because it's private (because TimeStampedStringPrinter privately inherits StringPrinter). This is good, because the intent of TimeStampedStringPrinter is to print timestamped strings. It would defeat the purpose if the user were able to call the non-timestamped printString() that is used internally by TimeStampedStringPrinter.
 
  • #13
Here is a simple program that demonstrate I can change y in BaseClass even it is declared private in DerivedClass. I just put in one program. It is easy to separate to .h files and all.
C++:
#include <iostream>
using namespace std;
class BaseClass
{
public: int y;
};

class DerivedClass : private BaseClass//Declares private 
{
public:
    BaseClass Bobj;
    void setY(int a) { Bobj.y = a; }
    int getY() { return Bobj.y; }
};

int main()
{//This shows I can get to y directly in BaseClass and change y.
    BaseClass B;//To direct access to y
    DerivedClass D;
    B.y = 10;
    cout << " Setting y to 10 directly to object B, B.y= " << B.y << "\n\n";
    D.setY(100);//Going through DerivedClass to 
    cout << " Setting y through DerivedClass, D.getY()= " << D.getY() << "\n\n";
    return 0;
}

I first set y directly from main to 10, then I call D to change y to 100. This is the printout:
Inheritance private test.jpg


Thanks
 
  • #14
yungman said:
Here is a simple program that demonstrate I can change y in BaseClass even it is declared private in DerivedClass. I just put in one program. It is easy to separate to .h files and all.
C++:
...
class DerivedClass : private BaseClass//Declares private
{
public:
    BaseClass Bobj;
    void setY(int a) { Bobj.y = a; }
    int getY() { return Bobj.y; }
};
But in this example, DerivedClass has a BaseClass member variable (in addition to inheriting BaseClass). You are modifying the y variable of the BaseClass member variable Bobj. You are not modifying the y variable of DerivedClass's parent!

If I instantiate a DerivedClass called myObject:
C++:
DerivedClass myObject;
then the following are two entirely separate variables:
C++:
// inaccessible due to private inheritance
myObject.y

// accessible because Bobj is a public member of myObject, and y is a public member of Bobj:
myObject.Bobj.y
Note that even if I get rid of Bobj and directly use the parent class's y, your example still works:
C++:
class BaseClass
{
public: int y;
};

class DerivedClass : private BaseClass
{
public:
    void setY(int a) { y = a; }
    int getY() { return y; }
};

int main()
{
    DerivedClass dc;
    dc.setY(5);
    int rslt = dc.getY();
    std::cout << rslt << std::endl;
    return 0;
}
But that should not be any surprise! The functions that set and get y are public. A similar example without inheritance would be
C++:
class MyClass
{
public:
    void setY(int a) { y = a; }
    int getY() const { return y; }
private:
    int y;
};

int main()
{
    MyClass mc;

    // I can use the set function to assign to mc.y:
    mc.setY(5);

    // I can use the get function to read mc.y's value:
    int rslt = mc.getY();

    // But I can't access y directly:
    mc.y = 10; // error
    int x = mc.y; // error
}
Of course you can always effectively bypass private by creating public get/set functions, but that is a poor design and I see no reason to do it.

In some cases there may be a good reason to have a public get **or** a public set for a private member variable, but generally not both, unless they are providing additional behavior beyond a simple read or write to the private variable.

Example: perhaps you want y to be publicly accessible, but you want your class to be thread-safe, so instead of making y public and thereby enabling it to be read/written directly, you might make y private, and create public get/set functions that lock a mutex before getting or setting the private variable.
 
Last edited:
  • Like
Likes yungman
  • #15
I think I see what you mean. Yes, I absolutely understand you cannot change y in DerivedClass object directly like D.y=100. I was talking about touching the y in object of BaseClass.

Sounds like the Base class access specification is just for the derived class object to declare the members inherited whether it's private or public.( of cause protected for another derived class).

Thanks
 
  • #16
jbunniii said:
Can you paste an example of this? If you want DerivedClass to construct BaseClass using the no-argument constructor of BaseClass, it is not necessary to call BaseClass() explicitly. However, if the BaseClass constructor requires arguments, then you will have to call the BaseClass constructor in the definition of DerivedClass, like this:
C++:
#include <iostream>
#include <string>

class BaseClass
{
public:
    BaseClass(int myInt, const std::string& myStr) : n(myInt), s(myStr) {}
    int intVal() const { return n; }
    std::string strVal() const { return s; }
private:
    int n;
    std::string s;
};

class DerivedClass : public BaseClass
{
public:
    DerivedClass() : BaseClass(5, "hello") {}
};

int main()
{
    DerivedClass derivedObj;
    std::cout << derivedObj.intVal() << " " << derivedObj.strVal() << std::endl;
    return 0;
}

I am reading constructor of the DerivedClass object. so far, both the book and your example shows constructor Object of DerivedClass calling the constructor of the BaseClass. I understand the Object can call anyone of the constructor of BaseClass like
C++:
DerivedClass() : BaseClass(){}//calling default constructor of BaseClass
DerivedClass() : BaseClass(x, y){}//calling constructor of BaseClass with 2 agruments
DerivedClass(int a, int b){}//Do I have a choice NOT calling constructor of BaseClass
Basically, it's just adding the constructor into the constructor of DerivedClass.

My question is whether I have a choice NOT TO call any constructor from BaseClass and just have constructor like line 3? Or I HAVE to attach all the constructor of the DerivedClass to one of the constructor of the BaseClass?

Thanks
 
  • #17
I put this in a separate post as the other one is very long already.
From this program in the book, it almost telling me that Cube constructor calling constructor of Rectangle to literally combine all the parameters of both together:
C++:
#include <iostream>
using namespace std;

class Rectangle
{
private:

public:
    double width, length;//Move to public
    Rectangle()
    {
        width = 0; length = 0;
    }
    Rectangle(double w, double len)
    { 
        width = w;
        length = len;
    }
    double getWidth() const
    {
        return width;
    }
    double getLength() const
    {
        return length;
    }
    double getArea() const
    {
        return width * length;
    }
};
class Cube : public Rectangle
{
protected: double height, volume;
public:
/*    Cube() : Rectangle()
    {
        height = 0.0;
        volume = 0.0;
    }
    Cube(double w, double len, double h): Rectangle(w, len)
    {
        height = h;
        volume = getArea() * h;
    }*/
    Cube()
    {
        height = 0.0;
        volume = 0.0;
        width = 0.0;
        length = 0.0;

    }
    Cube(double w, double len, double h) //: Rectangle(w, len) remove this
    {
        height = h;
        length = len;
        width = w;
        volume = getArea() * h;
    }
    double getHeight() const
    {
        return height;
    }
    double getVolume() const
    {
        return volume;
    }
};
int main()
{
    double cubeWidth, cubeLength, cubeHeight;
    cout << " Enter width: ";
    cin >> cubeWidth;
    cout << " Enter length: ";
    cin >> cubeLength;
    cout << " Enter height: ";
    cin >> cubeHeight;
    Cube myCube(cubeWidth, cubeLength, cubeHeight);

    cout << " Here are the Cube properties:\n";
    cout << " Width = " << myCube.getWidth() << ", length= " << myCube.getLength() <<
        ", height= " << myCube.getHeight() << "\n\n";
    cout << " The base area= " << myCube.getArea() << ", volume= " <<
        myCube.getVolume() << "\n\n";
    return 0;
}
See comments on line 35 and line 40. Basically just adding height and volume parameter together with the Rectangle constructors. Is this just to save some typing?
Is it better to just put all the parameters in constructor of Cube and forget using constructor from Rectangle. All I did is put width and length in Rectangle in public.

Then I remove line 36 to 45 of the original constructor that piggy back to Rectangle(). Then just put an independent constructor in line 46 to 53. Totally free from the Rectangle constructor().

Then I got rid of : Rectangle(w, len) in line 54.What's wrong doing it this way? It's stupider, not as elegant, but works. I just decoupled the constructor of Rectangle from Cube all together. It is much cleaner to me.
 
Last edited:
  • #18
yungman said:
See comments on line 35 and line 40. Basically just adding height and volume parameter together with the Rectangle constructors. Is this just to save some typing?
No. I don't think you're understanding the idea behind inheritance. The base class is very generic, and contains a limited set of data members and functions. Inherited classes get more specific, but still have all the attributes of the more general base class, plus whatever additional attributes and functionality that a derived class has.
yungman said:
Is it better to just put all the parameters in constructor of Cube and forget using constructor from Rectangle. All I did is put width and length in Rectangle in public.
No, it's worse. You might as well not have inheritance at all. Inheritance is used for hierarchical structures; for example in biology or in business organizations.
Consider a corporation with employees.
The base class is Employee - with such attributes as hire date and the department in which he or she works.
Derived classes could be Managers, Engineers, Support Staff. Each would be an employee in some departments (base class attributes), but would also have a salary, and whatever other data someone might want to associate with these categories.
Each of the above derived classes might be subdivided again by geographical location (different divisions of the company).

yungman said:
Then I remove line 36 to 45 of the original constructor that piggy back to Rectangle(). Then just put an independent constructor in line 46 to 53. Totally free from the Rectangle constructor().
Again, you are completely missing the point of what class inheritance is all about. With the very simple programs you see in the textbook, it doesn't seem like a lot of wasted effort to just add a few extra lines and completely skip the base class, but in the real world you might not be able to do this. What if the base class members were private, and could only be accessed by a protected base class constructor?
yungman said:
What's wrong doing it this way? It's stupider, not as elegant, but works.
But it won't always work, as I described above.
 
  • Like
Likes yungman
  • #19
yungman said:
I am reading constructor of the DerivedClass object. so far, both the book and your example shows constructor Object of DerivedClass calling the constructor of the BaseClass. I understand the Object can call anyone of the constructor of BaseClass like
C++:
DerivedClass() : BaseClass(){}//calling default constructor of BaseClass
DerivedClass() : BaseClass(x, y){}//calling constructor of BaseClass with 2 agruments
DerivedClass(int a, int b){}//Do I have a choice NOT calling constructor of BaseClass
Basically, it's just adding the constructor into the constructor of DerivedClass.

My question is whether I have a choice NOT TO call any constructor from BaseClass and just have constructor like line 3? Or I HAVE to attach all the constructor of the DerivedClass to one of the constructor of the BaseClass?

Thanks
If BaseClass has a no-argument constructor, then the DerivedClass constructor will call that constructor unless you specify otherwise. So the following are equivalent:
C++:
// DerivedClass constructor explicitly calls BaseClass no-argument constructor
DerivedClass() : BaseClass() {}

// DerivedClass constructor implicitly calls BaseClass no-argument constructor
DerivedClass() {}
This remains true even for the no-argument DerivedClass constructor:
C++:
// this will implicitly call the no-argument constructor of BaseClass
DerivedClass(int a, int b) {}

// If you want to call a different BaseClass constructor,
// you have to do it explicitly, for example:
DerivedClass(int a, int b) : BaseClass(a, b) {}
If BaseClass does not have a no-argument constructor, then the DerivedClass constructor must explicitly call a BaseClass constructor. Example:
C++:
class BaseClass
{
public:
    BaseClass(int a, int b) { x = a; y = b; }
private:
    int x;
    int y;
};

class DerivedClass
{
public:
    DerivedClass() {} // illegal; must call a BaseClass constructor
    DerivedClass() : BaseClass(0, 0) {} // OK, calls BaseClass(int a, int b)
    DerivedClass(int a, int b) {} // illegal; must call a BaseClass constructor
    DerivedClass(int a, int b) : BaseClass(a, b) {} // OK, calls BaseClass(int a, int b)
};
 
  • Like
Likes yungman
  • #20
yungman said:
C++:
class Cube : public Rectangle
From just this line, it can be observed that this is not an appropriate use of inheritance. If DerivedClass inherits from BaseClass, then DerivedClass should be a specific kind of BaseClass. A cube is not a specific kind of rectangle; it is not a rectangle at all.

However, if you want to proceed with making `Cube a subclass of Rectangle, you should make width and length protected instead of public. If you make them public, then they will be accessible to anyone who instantiates a Rectangle, which defeats the purpose of having getWidth and getHeight functions.
C++:
class Rectangle
{
protected:
    double width, length; // these are accessible by derived class Cube
public:
    Rectangle()
    {
        width = 0; length = 0;
    }
    Rectangle(double w, double len)
    {
        width = w;
        length = len;
    }
    double getWidth() const
    {
        return width;
    }
    double getLength() const
    {
        return length;
    }
    double getArea() const
    {
        return width * length;
    }
};
 
Last edited:
  • Like
Likes yungman
  • #21
jbunniii said:
From just this line, it can be observed that this is not an appropriate use of inheritance. If DerivedClass inherits from BaseClass, then DerivedClass should be a specific kind of BaseClass. A cube is not a specific kind of rectangle; it is not a rectangle at all.

However, if you want to proceed with making `Cube a subclass of Rectangle, you should make width and length protected instead of public. If you make them public, then they will be accessible to anyone who instantiates a Rectangle, which defeats the purpose of having getWidth and getHeight functions.
C++:
class Rectangle
{
protected:
    double width, length; // these are accessible by derived class Cube
public:
    Rectangle()
    {
        width = 0; length = 0;
    }
    Rectangle(double w, double len)
    {
        width = w;
        length = len;
    }
    double getWidth() const
    {
        return width;
    }
    double getLength() const
    {
        return length;
    }
    double getArea() const
    {
        return width * length;
    }
};
Thanks for the reply, this is straight out from the book by Gaddis.
https://cplusplushelp.weebly.com/uploads/2/5/6/5/25655197/0136022537.pdf

Look at page 890 program 15.5.

Not only that, there is a MISTAKE in the book on program 15.3 page 882. The function adjustScore() in line 38 to 45 is wrong. It is missing the else line to make it work:
C++:
    void adjustScore()
    {
        double fraction = score - static_cast<int>(score);
        if (fraction >= 0.50)
        {    score += (1.0 - fraction);    }
        else score = static_cast<int>(score);//This is missing in the book, won't work without this.
    }

Gaddis is a very good book...up to Chapter 13. Chapter 14 on Overloading operator is kind of iffy. Chapter 15 is not better. Maybe it's the last chapter, it's getting sloppy.

Thanks

Edit:

I want to also know how important is Inheritance and Polymorphism in real programming world ? It's like I read people already saying things like Overloading is not used that much in real life . I want to know how serious I should take these. I believe in making it simple if all possible. Rather writing a few more lines than to make it confusing.

thanks
 
Last edited:
  • #22
Another reason why I don't like this example is that a cube doesn't have different lengh, width and height! I'd rather call this object a Box, which is more general. One could say that a Box contains a base and a height, and therefore it would be better for a Box to have a Rectangle (its base) and a double (its height) as members.

One could derive a class Square which inherits from Rectangle, because a square is a kind of rectangle: one that has the same length and width. Similarly, one could derive Cube from class Box.

Here's a quick bare-bones example. It's probably not the best way to do this because it's been a long time since I learned about inheritance, and my knowledge is "rusty." And I never got very deep into inheritance in the course that I taught. I leave class Cube as an exercise. :wink:

C++:
// shapes.cpp - crude examples of deriving classes from other classes
// using composition versus inheritance

#include <iostream>

using namespace std;

// ----------------------------------------------------------------
// A rectangle is a two-dimensional object with (in general)
// different length and width.

class Rectangle
{
private:
    double width, length;
public:
    Rectangle (double w0, double l0)
      : width (w0), length (l0) {}
    double Area ()
    {
        return width * length;
    }
    friend ostream& operator<< (ostream& out, const Rectangle& r)
    {
        out << "(w = " << r.width << ", l = " << r.length << ")";  
        return out;
    }
};

// ----------------------------------------------------------------
// A box is a three-dimensional object that contains a rectangle as
// its base, and a number as its height.

class Box
{
private:
    Rectangle base;
    double height;
public:
    Box (double w0, double l0, double h0)
      : base (w0, l0), height (h0) {}
    Box (Rectangle b0, double h0)
      : base (b0), height (h0) {}
    double Volume ()
    {
        return base.Area () * height;
    }
    friend ostream& operator<< (ostream& out, const Box& b)
    {
        out << "(base = " << b.base << ", h = " << b.height << ")";
        return out;
    }
};// ----------------------------------------------------------------
// A square is a kind of rectangle, whose length and width are
// equal.  The Square constructor invokes the Rectangle constructor,
// setting the length and width equal.

class Square : public Rectangle
{
public:
    Square (double s0)
      : Rectangle (s0, s0) {}
};

// ----------------------------------------------------------------

int main ()
{
    Rectangle myRect (3, 4);
    cout << "myRect is " << myRect << endl;
    cout << "Its area is " << myRect.Area () << endl << endl;
    Box box1 (1, 2, 3);
    cout << "box1 is " << box1 << endl;
    cout << "Its volume is " << box1.Volume () << endl << endl;
    Box box2 (myRect, 5);
    cout << "box2 is " << box2 << endl;
    cout << "Its volume is " << box2.Volume () << endl << endl;
    Square mySquare (6);
//  Class Square doesn't define its own Area() or operator<<();
//  it inherits them from class Rectangle.
    cout << "mySquare is " << mySquare << endl;
    cout << "Its area is " << mySquare.Area () << endl << endl;
    return 0;
}

Output:
Code:
% g++ shapes.cpp -o shapes
% ./shapes
myRect is (w = 3, l = 4)
Its area is 12

box1 is (base = (w = 1, l = 2), h = 3)
Its volume is 6

box2 is (base = (w = 3, l = 4), h = 5)
Its volume is 60

mySquare is (w = 6, l = 6)
Its area is 36
 
Last edited:
  • Like
Likes jbunniii and Vanadium 50
  • #23
This is a bad example for sure, but we've complained about this book before.

As you say, a cube is not a cube and while it might be a box, a box is not a rectangle.

You could have the hierarchy that box inherits from volume, or rectangle inherits from polygon. My experience, however, is that such "data" inheritances, which might make sense in designing a database, tend not to be so helpful when writing actual code. What tends to be more helpful is "method" inheritance: for example, all objects that need to be written to disk might inherit from "Storable", which is where the actual read/write code lives.

Or "2DHistogram" and "1DHistogram" might inherit from "Histogram", but it's not because of similarity of the data. It's a similarity in what you do with it, like how you display it. (e.g. how you treat an axis)
 
  • #24
yungman said:
Not only that, there is a MISTAKE in the book on program 15.3 page 882. The function adjustScore() in line 38 to 45 is wrong. It is missing the else line to make it work:
No, adjustScore() is not wrong. The function adjusts score if the fraction part is > 0.5. Otherwise it doesn't alter score. An if statement is not required to have an else clause.
if statement (without else) - choose to perform an action vs not performing it
if - else statement - choose between two actions
You might want to review the first two sections of Ch. 4.
yungman said:
It's like I read people already saying things like Overloading is not used that much in real life .
You keep saying this, and many people have responded here with counter arguments, including the fact that you have been using overloaded operators since Ch. 2 of Gaddis. If someone writes a C++ program that doesn't have any user-written classes or other user-defined types, then overloaded operators are pretty much irrelevant. Someone writing operating system kernel code is probably not going to be using overloaded operators, but there are plenty of other types of coding where it would be useful.

What seems like your main argument against overloaded operators
"Rather writing a few more lines than to make it confusing."
is also an argument against writing functions.
Would you also advise someone learning C++ or C or Java or Python that it would be better for them to "write a few more lines" rather than "make it confusing" by putting a single block of code in one place?

jtbell said:
Another reason why I don't like this example is that a cube doesn't have different lengh, width and height! I'd rather call this object a Box, which is more general.
Yeah, that inheritance bothered me as well, as a cube has all three dimensions equal, unlike a rectangle.
 
  • #25
Vanadium 50 said:
This is a bad example for sure, but we've complained about this book before.
.....
The first 13 chapters were very good, 14 on overloading is iffy, I had to watch video on youtube and read a lot, experimented a lot and asked here to get a good picture.
 
  • #26
jtbell said:
.....
Here's a quick bare-bones example. It's probably not the best way to do this because it's been a long time since I learned about inheritance, and my knowledge is "rusty." And I never got very deep into inheritance in the course that I taught. I leave class Cube as an exercise. :wink:
......
What is the reason?

Reason I ask is I almost finish the Inheritance part of the chapter. It is not difficult to follow at all. Just after I go through the exercise and read through it, it is really not very clear on a lot of things. Like what can Inheritance do where Aggregation and Collaboration cannot do?

If I want to understand better, I have to go through like what I went through playing with your program, watch more video and all. There comes the question, is it that important to learn this? Not in academic point of view, but actually in real life work situation.

Which lead to the ultimate question, at what point I am consider I study the minimum of C++, that I can learn as needed in the future. I know, there is a whole lot more in C++, but I am finishing up the whole book of the Brief version of Gaddis, The long version mostly cover data structure, with one chapter in Template and library.

I already bought the book on gaming and graphics by Gaddis, I want to look into this book, learning C++ is getting very dry. 15 chapters, still running in cmd window.

Thanks
 
Last edited:
  • #27
yungman said:
Which lead to the ultimate question, at what point I am consider I study the minimum of C++, that I can learn as needed in the future.
This depends entirely on what you want/need to do with C++.

For example, if you were going to work on similar projects to what I do at work (primarily system level programming, including several iOS daemons and related libraries, and various command line analysis tools), you would need to be comfortable with substantial parts of the C++ standard library, including but not limited to: most of the classic C++98 library including all of the main containers (vectors, maps, sets, queues, etc.), as well as more modern additions such as smart pointers, optionals and variants, threads, mutexes, condition variables, string streams, std::filesystem.

You would also need to be comfortable with most of the modern C++ (11/14/17) language additions, such as rvalue references/move semantics, lambdas, auto, range-based for loops, structured bindings, etc.

Also, of course you need a solid understanding of "classic" C++ features such as templates, inheritance, namespaces, exceptions, iterators, etc.

And we're about to open the floodgates on C++20 now that most of us are using compiler versions that support it, so that will involve learning about the main new features: concepts, modules, coroutines, ranges, etc. (I need to learn a lot of these myself, as I haven't used any of them yet!)
 
  • #28
yungman said:
What is the reason?
Not enough time. It was at the end of a two-semester course that wasn't a "C++ course" with the goal of teaching everything about C++. It was an "intro to programming" course that used C++. It included some topics that weren't strictly C++, e.g. analysis of different sorting techniques (I always did at least one "simple sort" along with quicksort), and linked lists (which was my excuse for introducing pointers).

Looking back at a PDF of my old textbook, I see that inheritance was in the very last chapter. I may even have used only the first part of that chapter, because I don't remember using the examples in the later parts of it.

I don't think I ever taught a course (physics or CS) in which I covered the entire textbook. I always had to omit or skim lightly over stuff in order to cover the foundations at a pace my students could handle. I wasn't at a school like MIT. :wink:
 
Last edited:
  • #29
jbunniii said:
This depends entirely on what you want/need to do with C++.

For example, if you were going to work on similar projects to what I do at work (primarily system level programming, including several iOS daemons and related libraries, and various command line analysis tools), you would need to be comfortable with substantial parts of the C++ standard library, including but not limited to: most of the classic C++98 library including all of the main containers (vectors, maps, sets, queues, etc.), as well as more modern additions such as smart pointers, optionals and variants, threads, mutexes, condition variables, string streams, std::filesystem.

You would also need to be comfortable with most of the modern C++ (11/14/17) language additions, such as rvalue references/move semantics, lambdas, auto, range-based for loops, structured bindings, etc.

Also, of course you need a solid understanding of "classic" C++ features such as templates, inheritance, namespaces, exceptions, iterators, etc.

And we're about to open the floodgates on C++20 now that most of us are using compiler versions that support it, so that will involve learning about the main new features: concepts, modules, coroutines, ranges, etc. (I need to learn a lot of these myself, as I haven't used any of them yet!)
Thanks

I guess it will never end! I don't even know most of the stuffs you named. So stop after this chapter is as good a guess as any. At least I finish one book from cover to cover.
 
  • #30
yungman said:
I guess it will never end! I don't even know most of the stuffs you named. So stop after this chapter is as good a guess as any. At least I finish one book from cover to cover.
It's a big language, and huge when you include the standard library. At this point I doubt that there's anyone on earth, including Bjarne Stroustrup, who can claim to know the whole thing.

And it just keeps getting bigger. You can see this in the page counts for the various (draft) standards:

C++98: 748
C++11: 1310
C++14: 1354
C++17: 1572
C++20: 1829

One opening question I ask job applicants is to rate their C++ knowledge on a scale of 1 to 10. Based on my limited data sample, most strong candidates rate themselves as 7 or 8.

I've seldom heard anyone say 9 or 10 (nor would I rate own knowledge that high), and those that did were actually quite unknowledgeable but apparently failed to realize it.
 
  • #31
jtbell said:
Not enough time. It was at the end of a two-semester course that wasn't a "C++ course" with the goal of teaching everything about C++. It was an "intro to programming" course that used C++. It included some topics that weren't strictly C++, e.g. analysis of different sorting techniques (I always did at least one "simple sort" along with quicksort), and linked lists (which was my excuse for introducing pointers).

Looking back at a PDF of my old textbook, I see that inheritance was in the very last chapter. I may even have used only the first part of that chapter, because I don't remember using the examples in the later parts of it.

I don't think I ever taught a course (physics or CS) in which I covered the entire textbook. I always had to omit or skim lightly over stuff in order to cover the foundations at a pace my students could handle. I wasn't at a school like MIT. :wink:
I hope what I studied is a lot more than introduction of CS. I worked more than what the book shows, I know for fact from my grandson that took a C++ class in junior college for one semester, they used the Gaddis book, they barely covered to chapter 11 BUT totally skipped chapter 9 on Pointers. That's the hardest chapter of the first 11 chapters! The rest of the 10 chapters are easy. It's only after chapter 11 that it gets a lot harder. They did nothing on OOP.

Even follow straight from the book is not hard, I learn so much more playing with your program, really get into the the Three Amigos ( Constructor, Copy Constructor and Assignment Operator)! If I only follow the book, I'd learn nothing. The 3 Amigos are quite hard, it might look easy at the beginning, but I feel they are even harder than pointers. Those shallow copy with pointers are tricky, remember I kept at it until I made it failed?

I always make up programs and practice that are much harder than in the book. If I finish chapter 15, I can really say I studied the book from cover to cover.

Thanks for all your help.
 
  • #32
jbunniii said:
It's a big language, and huge when you include the standard library. At this point I doubt that there's anyone on earth, including Bjarne Stroustrup, who can claim to know the whole thing.

And it just keeps getting bigger. You can see this in the page counts for the various (draft) standards:

C++98: 748
C++11: 1310
C++14: 1354
C++17: 1572
C++20: 1829

One opening question I ask job applicants is to rate their C++ knowledge on a scale of 1 to 10. Based on my limited data sample, most strong candidates rate themselves as 7 or 8.

I've seldom heard anyone say 9 or 10 (nor would I rate own knowledge that high), and those that did were actually quite unknowledgeable but apparently failed to realize it.
Yeh, I am not trying to be the expert, I just want to know what is the minimum I have to study to reach a milestone. It's like how many semester to study for a college student on C++.
 
  • #33
yungman said:
I hope what I studied is a lot more than introduction of CS. I worked more than what the book shows, I know for fact from my grandson that took a C++ class in junior college for one semester,
One semester is not very long. One college where I taught for several years had a 3-quarter sequence, with the first quarter being C, and the second being C++ including classes. The third quarter was data structures and algorithms. Two quarters would be 20 weeks, about 33% longer than a semester.
 
  • #34
Mark44 said:
One semester is not very long. One college where I taught for several years had a 3-quarter sequence, with the first quarter being C, and the second being C++ including classes. The third quarter was data structures and algorithms. Two quarters would be 20 weeks, about 33% longer than a semester.
That sounds about right, like my grandson studied 10 chapters(skipping chapter on pointers), the book is 15 chapters, so it's like 50% more than a semester. Do you skip any topics? Data structure is in the complete book from chapter 16 to 20, but it's shorter chapters, so that should fit nicely in a quarter.
 
  • #35
yungman said:
Yeh, I am not trying to be the expert, I just want to know what is the minimum I have to study to reach a milestone. It's like how many semester to study for a college student on C++.
The best way to learn C++ beyond the basics is to use C++ by writing programs of your own, and/or modifying other people's programs, e.g. by adding new features.

Since you're working on your own as opposed to being employed in a software organization, a great way to proceed would be to find an interesting open source project. Make a local clone, read through the source code, look up stuff in books or online (cppreference is a good web site) until you understand how it works, then try adding some small features.

This way any future learning will be motivated by a specific need as opposed to just reading through an encyclopedic reference such as Stroustrup. In my experience, this is a much more efficient way to learn new language/library features than simply reading about them.
 

Similar threads

Replies
36
Views
3K
Replies
36
Views
4K
Replies
23
Views
2K
Replies
35
Views
3K
Replies
36
Views
2K
Replies
31
Views
2K
Replies
17
Views
2K
Replies
89
Views
5K
Replies
4
Views
2K
Back
Top