Class Questions

Pages: 12
Ok so here are some questions I have about classes, virtual, inheritance, objects etc.

1. Ok so when creating a class, say I have an abstract class that has variables in it, I want to use these variables in child classes, the best way i've see to do this is this:

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 Parent
{
    public:
        Parent(int x, std::string str) : m_x(x), m_str(str) {}
        virtual void Speak()
        {
            std::cout << "I am parent class!\n";
        }

        virtual void Test() = 0;

    protected:
        int m_x = {};
        std::string m_str = {};
};

class Child : public Parent
{
    public:
        Child(int x, std::string str) : Parent(x, str) {}
        void Speak() override
        {
            std::cout << "I am child class!\n";
        }

        void Test() override
        {
            std::cout << "Forced to be Overridden in child\n";
        }
};

void Output(Parent& parent)
{
    parent.Speak();
    parent.Test();
}

int main()
{
    Child child(8, "Ben");

    Output(child);
}


Is this the best way to access variables in child classes?

2.Do functions that derive from an interface class where the function is virtual have to have override in the derived class?

3. When creating objects for classes, virtual or not, should they be created on the stack or the heap?


Here are the notes I have currently taken, is the information i've written accurate: https://docs.google.com/document/d/11bb9kTBS_bnPAOP1B-Mgqt_gm-s9Zurqjjx32Wcpbqo/edit?usp=sharing
Last edited on
Rather than wondering "which way is best," you should understand the tradeoffs between different ways of doing things. After all, if one way was always best, then the other ways wouldn't exist.

To answer your questions:

1. It depends.
If you don't want outside code to access the members directly then protected members work well. If you do, then public members would be better. If member data is logically linked, then private data with accessors would be more appropriate.

2. It depends.
If the base class implementation of the virtual function is okay for the derived class, then the derived class doesn't need to override it.

3. It depends.
It takes much less time to allocate/deallocate space for items on the stack than on the heap. But the stack is typically much smaller than the heap. When allocating on the heap, you have to be sure to deallocate too. That's a lot easier now with unique_ptr<> and shared_ptr<> but you still have to choose the right one. My advice is to allocate on the stack unless you're sure that stack space will become a problem (such as with a large array in a recursive function). Even then, you can always tell the compiler to create a larger stack.
Ok, thank you.

Why create pointers to objects anyways? why do this:

1
2
3
Base base;
Derived derived;
Base* p_base = &derived;


Why make a pointer to derived?
For reasons why you would use a pointer, see my post in this thread:

http://www.cplusplus.com/forum/general/105593/

(Although note that many of these reasons are less useful in modern C++).

In your code, the reason is no. 4 - so that you can take advantage of polymorphism. You can have code that operates on a Base pointer to an object, and at run-time, when you call a virtual method on the pointer, this will resolve to the correct overridden method in the correct derived class.
Why create pointers to objects anyways?

In that example, apparently to complicate things; reference is simpler:
1
2
Derived derived;
Base& base = derived;

-- in fact, you used that already in the first post on this thread: Parent& parent is a reference to parent, which is bound to a Child.

In a bigger program, a pointer to a base may be needed if you're going to make a container (vector, array, etc) holding objects of different types all derived from the same Base, or a factory function that makes objects of different types all derived from the same base, based on some run-time input. Of course in a real program, such pointer would be std::unique_ptr<Base> rather than Base*.
Last edited on
So what exactly does a pointer/reference do to these objects and to other things in general? I know a pointer holds a memory address but why do that? I read somewhere its so everything is the same size in memory, and it makes things much easier to use, but maybe someone could clarify that.

Also arent references an alias for an object? and pointers are the actual memory address? why not actually get the actual address instead of an alias?
Last edited on
I know a pointer holds a memory address but why do that?

Did you read the post I directed you towards?

In the case of Polymorphism, it's because you need an object that was created as a Derived if you want to access the virtual methods of Derived. You don't want to create an actual Base object, because then it's only a Base, and it won't contain the overridden methods.

Using a Base pointer to a Derived object means that you can treat pointers as if they were Base objects, with the Base interface, but when you call the virtual methods on the pointer, they resolve to the overridden method on the Derived object.
Yes but what I mean is what is actually happening underneath the code, what is the technical aspects of pointing to an object, how does it work?

I've read a liitle bit on Binding, i'm not 100% up on so maybe that could be explained a little as well, but basically it just converts functions to memory addresses and other objects correct?, that would be early binding and virtual functions perform late binding which is done at runtime, does the pointer do early or late binding for non virtual objects?
Last edited on
Virtual functions bind late (i.e., at run time, not compile time).
The pointer points to the start of the object in memory.
The derived object's representation contains the base class's data and the derived class's data.
It also contains an extra hidden pointer to the virtual function table (vtable) for the derived class.
That table contains the addresses of the virtual member functions.
The calls to the virtual functions in the code are represented as offsets from the start of the vtable.
Last edited on
under the hood computer hardware doesn't give a rat's rear about your OOP design and code :)

Under the hood your stuff is at a location in memory, and a pointer is its location. Period. It all resolves down to that in the machine code, as machine code is not OOP all that is 'lost'. C++ compiler is going to boil all this junk back down to procedural code and raw pointers and byte-level statements. Your object is a pile of bytes out in memory, and that memory location is your pointer, its really that simple down at the hardware / technical level. The compiler has to unravel all these concepts down to that when it compiles your code; this high level syntax is not expressed directly. I mean, it turns virtual things into concrete, typed functions or items. It connects the correct instance of that to the user, via pointers (explicit or hidden). By the time it sees your base class / child class/ etc stuff, its all concrete and resolved down to effectively basic C like structs and C like functions and strung back together that way.

So what you are asking really is what the c++ compiler is doing when it translates your statements into machine language, or a step above that, how it internally tracks what you told it to do in your code?? Not sure what you want to know -- your last part of the question is very high level, and the first part is very low level.

In addition to what tpb said:

does the pointer do early or late binding for non virtual objects

There's no such thing as a "virtual object". Methods are virtual. Polymorphism - i.e. late binding - only happens with virtual methods, because those are the only methods that are included in the vtable. If you call a non-virtual method on a Base pointer, it will only ever call the Base method, even if a Derived method overrides it.

EDIT: And to follow up on what jonnin said: what is "happening underneath the code" is that your compiler is obeying the rules of the C++ standard. What it does under the hood - using a vtable - is just the way most (all?) compilers happen to implement the required behaviour, but it doesn't really affect the way you write code.
Last edited on
So what you are asking really is what the c++ compiler is doing when it translates your statements into machine language, or a step above that, how it internally tracks what you told it to do in your code?? Not sure what you want to know


Basically what I'm trying to say is I want a way to visualize whats happening in my head. I dont really understand whats happening because I cant visualize it. So for example with a string, stroing a string is easy to visualize:

std::string str = "String";

Basically I imaging str is a box holding that text, I can "See" that in my head so it makes sense. Hopefully that explains what im trying to get at, im terrible at explaining what i'm thinking sometimes.
So visualise it this way:

Any object with virtual methods contains a "lookup table" that knows exactly which virtual method, on which class, should be called. Even if you use a base class pointer to a derived object, that lookup table will be used to call the right overridden method.
Sure, the way I view virtual is one of those old time phone switchboards, the one someone sits in front of and connects the pin to the right hole where the call needs to go, thats how someone explained it to me and that makes perfect sense and I understand that, but the object itself is what im not quite understanding, but from what you said, basically:

Derived derived;

Base& = derived;

Base& stores a pointer to derived and when the virtual function is called it uses that basically as kind of a "Phone Book" to find its correct function at runtime?
@Ch1156, So you just ignored my explanation?
Last edited on
Base& stores a pointer to derived and when the virtual function is called it uses that basically as kind of a "Phone Book" to find its correct function at runtime?

Not really. The pointer is just a memory address. But the object it points to is a Derived object, and contains a vtable (or "switchboard") with the correct connections for a Derived object.

So, even though you're calling the method on a pointer of type Base*, the switchboard on the object connects it to the overridden Derived method, not the Base method.

The switchboard (vtable) isn't a property of the pointer. The switchboard is a property of the object it's pointing to.
Last edited on
The pointer is just a memory address. But the object it points to is a Derived object, and contains a vtable (or "switchboard") with the correct connections for a Derived object.


AH, Ok that I think did it, I can see it now, I was confused on how they all connected to each other.

@Ch1156, So you just ignored my explanation?


I'm sorry. when I refreshed there were like 4 or 5 questions, I must have missed it!
Last edited on
Partial disassembly of direct and virtual function calls:

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
#include <iostream>

class B {
public:
    virtual void f() { std::cout << "B::f()\n"; }
};

class D : public B {
public:
    void f() override { std::cout << "D::f()\n"; }
};

int main() {
    B b;
    D d;
    B* p = &d;

    b.f();
    /*
	leaq	-32(%rbp), %rax    # get address of b
	movq	%rax, %rdi         # load it as calling object's addr
	call	_ZN1B1fEv          # call member function directly
    */

    p->f();
    /*
	movq	-16(%rbp), %rax    # get value of p
	movq	(%rax), %rax       # deref to get addr of d
	movq	(%rax), %rax       # deref to get first entry in vtable
	movq	-16(%rbp), %rdx    # get value of p (again)
	movq	%rdx, %rdi         # load it as calling object's addr
	call	*%rax              # call member function indirectly
    */
}

ah, I didnt mean visualize it as in assembly, seeing the actual instructions in machine code, I meant associating what the code is doing with something in the real world, so storing a word in a variable is equal to putting a shoe in a box, with the box being the variable name and the shoe being the word. The way MikeyBoy explained how they interact I think clarified it for me but we'll see if I understand like I think I do.
Last edited on
I give up. :-(
Pages: 12