Why Have Functions() in classes?

Pages: 12
One could have two types:
1
2
3
4
5
6
7
8
struct Point {
  float x, y, z;
};

struct Atom {
  float x, y, z;
  std::string name;
};

That, however, is unnecessary repetition particularly if Atom is used like a Point somewhere.

First step of reuse is obviously to compose:
1
2
3
4
struct Atom {
  Point location;
  std::string name;
};

Now it is clear that Atom HAS-A Point, but passing the Point-part to functions that operate on Points is awkward.

We can reuse more tightly in C++:
1
2
3
struct Atom : public Point {
  std::string name;
};

Now Atom inherits Point. Effectively Atom IS-A Point (that happens to carry a string along). We can give an Atom to anyone, who expects a Point.
That is the trivial part of inheritance.

An another part is virtual member functions. Each struct that has virtual members does have a hidden table of function pointers. The function calls dereference the table to connect to correct implementation for the real type of the object. This produces runtime polymorphism.

Templates allow autogeneration of overloaded functions for new types. They represent compile-time polymorphism.
C++ is not solely OOP usage. If the task can be done by procedural means, then C++ can do it. Though C++ can and does augment/change how things are done in C.

How are random numbers generated in C? One possibility:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
   int i;
   int num;

   srand(time(0));

   for (i = 0; i < 10; ++i)
   {
      num = rand() % 25 + 1;
      printf("%d  ", num);
   }
   
   puts("\n");
}

That code could be compiled as-is as C++ and still work.

Though recommended is to use the C header names C++ has.
1
2
3
#include <cstdio>
#include <cstdlib>
#include <ctime> 

Generating random numbers, the C++ way.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>  // C++ I/O, <stdio.h>
#include <random>    // C++ random numbers, <stdlib.h>
#include <chrono>    // C++ time, <time.h>

int main()
{
   // create a pseudo-random engine
   std::default_random_engine prng;

   // create a time seed
   unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();

   // seed the engine
   prng.seed(seed);

   // create a distribution to clamp the random numbers to a range
   std::uniform_int_distribution<int> dis(1, 25);

   for (int i = 0; i < 10; ++i)
   {
      // generate a random number
      int num = dis(prng);

      // display the number
      std::cout << num << "  ";
   }
   std::cout << std::endl;
}

That looks intimidating, and compared to C it can be.

C++ suffers from an embarrassment of riches for generating random numbers. Including to natively generate floating point numbers, not just integers.
https://cplusplus.com/reference/random/

Another C++ version of generating random numbers:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <random>

int main()
{
   // create a pseudo-random engine and seed it
   std::default_random_engine prng { std::random_device {} () };

   // create a distribution to clamp the random numbers to a range
   std::uniform_int_distribution<int> dis(1, 25);

   for (int i { }; i < 10; ++i)
   {
      // generate a random number, display the number
      std::cout << dis(prng) << "  ";
   }
   std::cout << '\n';
}


C++ random number facilities were introduced in C++11:
https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3551.pdf

Generating random numbers using C procedures in new C++ code can have problems:
https://web.archive.org/web/20180123103235/http://cpp.indi.frih.net/blog/2014/12/the-bell-has-tolled-for-rand/

Bashing together a custom header-only toolkit is not that hard, it was one of the first things I did years ago with help of that working group paper and several people here at CPlusPlus. Having a custom header can make using the C++ random engines as easy as using C's srand/rand functions.
I'd bet good money a seasoned C programmer would find my C code to be less than perfect.

I admit I don't usually work with C. :)
Thanks kskiverto and georgeP,
so, I think inheritance is the ability of a new class definition to include ALL of the base class members like when a C struct includes a item that is a previously-defined struct. Like "nesting" of structures, inheritance is "nesting" of class definitions. I also assume the class statement is like a typedef-struct in C where it is like a template and requires another statement to actually create an object of that class. A C struct cannot include functions (methods) but a C++ class can.

Is it true that methods in one class can only operate on the members of that class? Can methods created outside of a class reference public members of a class directly, of does it have to use a method in that class to "get/set" them?
Object-Oriented Programming with ANSI-C by Axel-Tobias Schreiner

Worth looking over just to dispell some myths.
Is it true that methods in one class can only operate on the members of that class?


Nope. Static members don't even require a class variable/instance to be used. but you can certainly operate only off parameters just like anything else. This may be a dubious design most of the time ... there are a great many things you can do and probably usually wouldn't in both C and C++.

class c
{
static int duh(int x) {cout << x*42 << endl;}
}
...

c::duh(3);
c obj{};
obj.duh(4);


Can methods created outside of a class reference public members of a class directly

Sort of. Remember that in c++ a class is a TYPE (in C its a weird grouped variable created on the spot that has to be FORCED into acting like a type). It makes no sense to try to modify a class variable directly because its a part of a type, not an existing variable. A static class variable can be treated this way, but note that the value is the same for ALL instances, one variable, shared across all. You can certainly modify the public variables of a variable of type someclass 'directly' though, as in x.variable = value;

The get set methods are recommended by many. In some cases it is eggheadery, protecting yourself from yourself and sort of idiotic for smaller programs where it brings nothing to the table except a bulk ton of clutter. There are 2 big (real, not just OOP theory) reasons why its done:
1) when the getters and setters DO something besides get and set. This can be a layer of protection, like returning a copy of the value (which can be modified without affecting the original), or like validation for the case of input/modifications, whatever.
2) consistency. It is highly likely in a large program you will have #1 above for some values. At that point, you can either have a 'guess which way' set of objects where whether something has a getter/setter depends on the value and the whim of the developer, or you can sigh at the mess and put one on everything. Even microsoft got irritated with this: many of their MFC objects actually have BOTH direct access to the values AND functions to do the same, specifically their point/rectangle/etc gui positioning stuff has a dozen ways to change just a couple of values.

there are also 3) and 4) which are less practical and more theoretical... encapsulation and data hiding ideas (and there may be a 5, 6, 7.. as well, its been a long time for me since I dealt with the theoretical design stuff) which do have practical applications, but as often as not, its just 'because someone said so' for a specific design -- the things they are solving are non-issues more often than not.
Last edited on
To illustrate how inheritance works let's show a couple of examples. First, a basic class. The Base Class that other classes can derive from.

Fair warning, this will be spread across multiple postings, and there will be some delay between each one since I'm "krufting" them on the fly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

class Fish
{
public:
   bool FreshWaterFish;

public:
    Fish() { FreshWaterFish = false; }
   ~Fish() { }

public:
   void Swim();
};

int main()
{
   Fish myFish;

   std::cout << "Getting the fishies to swim...\n\n";

   std::cout << "Fish: ";
   myFish.Swim();
}

void Fish::Swim()
{
   if (true == FreshWaterFish)
   {
      std::cout << "Swims in a lake...\n";
   }
   else
   {
      std::cout << "Swims in the sea...\n";
   }
}

The class is Fish, this is a new type. Just like with int or other built-in types to use the type you have to create one or more instances of it. Line 18.

The type consists of a single member data (FreshWaterFish), a constructor that assigns a value to the data type, a destructor and a member function (method) that prints out a message.

To print out the message you use the dot operator on the instance and invoke the method. Line 23.

Lines 26-36 are defining the method declared in the class definition on line 13.

Why all the public: uses in the class definition? By default, the opposite of a C/C++ struct is any item in a class is created as being private. If the public: section above Swim() was private: trying to invoke the method would fail at compile time.
Getting the fishies to swim...

Fish: Swims in the sea...

While that is a decent first pass design for a base class it could be improved. Overload the constructor by adding a new version that takes a bool and sets the data member when the instance is constructed.

Plus, a data member should be private so it isn't accessible outside of an instance of the class, with at a bare minimum a couple of methods that can set or get the status of the data member.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>

class Fish
{
private:
   bool FreshWaterFish;

public:
    Fish()           { FreshWaterFish = false; }
    Fish(bool water) { FreshWaterFish = water; }
   ~Fish() { }

public:
   bool GetWater() const     { return FreshWaterFish; }
   void SetWater(bool water) { FreshWaterFish = water; }

public:
   void Swim();
};

int main()
{
   Fish myFish1;
   Fish myFish2(true);

   std::cout << "Getting the fishies to swim...\n\n";

   std::cout << "Fish 1: ";
   myFish1.Swim();

   std::cout << "Fish 2: ";
   myFish2.Swim();
}

void Fish::Swim()
{
   if (true == GetWater())
   {
      std::cout << "Swims in a lake...\n";
   }
   else
   {
      std::cout << "Swims in the sea...\n";
   }
}
Getting the fishies to swim...

Fish 1: Swims in the sea...
Fish 2: Swims in a lake...
M'ok, you've created a base class, Fish. Let's now create a couple of derived classes that inherit from Fish, Carp & Tuna.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>

class Fish
{
private:
   bool FreshWaterFish;

public:
   Fish(bool IsFreshWater) : FreshWaterFish(IsFreshWater) { }

public:
   void Swim();
};

class Tuna : public Fish
{
public:
   Tuna() : Fish(false) { }

   void Swim();
};

class Carp : public Fish
{
public:
   Carp() : Fish(true) { }

   void Swim();
};


int main()
{
   Tuna myTuna;
   Carp myCarp;

   std::cout << "Getting my fishies to swim...\n\n";

   std::cout << "Tuna: ";
   myTuna.Swim();

   std::cout << "Carp: ";
   myCarp.Swim();
}

void Fish::Swim()
{
   if (FreshWaterFish)
   {
      std::cout << "Swims in a lake...\n";
   }
   else
   {
      std::cout << "Swims in the sea...\n";
   }
}

void Tuna::Swim()
{
   std::cout << "Swims real fast...\n";
}

void Carp::Swim()
{
   std::cout << "Swims real slow...\n";
}
Getting my fishies to swim...

Tuna: Swims real fast...
Carp: Swims real slow...

One thing to note, the Swim method in Carp and Tuna over-ride (hide) Fish's Swim method.

This is a quick and very dirty look at what inheritance is all about.
One last thing, the order of construction and destruction in base and derived classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>

class Fish
{
public:
    Fish() { std::cout << "Fish ctor!\n"; }
   ~Fish() { std::cout << "Fish dtor!\n"; }
};

class Carp : public Fish
{
public:
    Carp() { std::cout << "Carp ctor!\n"; }
   ~Carp() { std::cout << "Carp dtor!\n"; }

public:
   void Swim();
};

int main()
{
   Carp myCarp;

   std::cout << "\nCarp: ";

   myCarp.Swim();
}

void Carp::Swim()
{
   std::cout << "Swims real slow...\n\n";
}
Fish ctor!
Carp ctor!

Carp: Swims real slow...

Carp dtor!
Fish dtor!

These class designs are really rude, crude and lewd. Pared down to bare essentials. I kinda cringe at writing them. But doing better class design would be more complicated. :)

Note: object construction and destruction is handled automatically. When the object goes out of scope, the program ends, the created object is destroyed without any intervention required by the programmer.
Last edited on
I may touch on polymorphism a bit later. RL takes precedence. :Þ
Now I am confused. But, I just received a new book "The C++ Programming Language, 4th Edition" by Bjarne Stroustrup. 1,347 pages. Weighs 4 pounds. If I read 1 page a day I will be 3-1/2 years older by the time I finish. And, did I mention it's small print? Yikes!
That book is more of a reference tome than something to learn C++ from. Useful for someone already conversant with C++.

A book more suited to learn from by the same author is his "Programming: Principles and Practice Using C++." Though I wouldn't really recommend it. *shrug*

A book for learning C++, C++20 at that though it still covers "older C++", is "Beginning C++20: From Novice to Professional".
https://www.amazon.com/gp/product/1484258835/
@robertb

Yes, you can do OOP with C. The point of C++ is that it supposed to simplify that task. Particularly that functions are 'tied' to an object (i.e. the this pointer is implicitly passed to those functions) and that special functions like contructor and destructor exists.
"The C++ Programming Language, 4th Edition" by Bjarne Stroustrup.


That only covers C++11. There have been 3 new standards since then (C++14, C++17 and C++20) and a 4th in progress (C++223). It is definitely not a book from which to learn C++.

Re "Programming: Principles and Practice Using C++.". I'm with George on this and wouldn't recommend it outside of a course based around it. Also it only covers C++14. I would also recommend the "Beginning C++20: From Novice to Professional" book from which to learn up-to-date C++.

Also the website https://www.learncpp.com/
That only covers C++11.

Better C++11 than either C++98/03 or no standard at all. :)

C++11 is widely considered "Modern C++". Maybe with the sweeping changes C++20 made to the language that title should be moved up to C++20.

Not to disparage C++14/17, there were quite nice change/additions made to the language.

I do understand a lot of schools and companies are loath to adopt newer standards and compilers capable of using those standards. Time and money and institutional inertia still make a lot of decisions.
Topic archived. No new replies allowed.
Pages: 12