compilation

hello,

Can someone please explain the output of the code below?
I would be greatful if someone explain it row by row.

Thank you very much.
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
#include <stdio.h>

class A{
public:
	A(){
		printf("a\n");
		f1();
		f2();
	}
	void f1(){
		printf("f1:a\n");
	}
	virtual void f2(){
		printf("f2:a\n");
	}
};

class B: public A {
public:
	B(){
		printf("b\n");
		f1();
		f2();
	}
	void f1(){
		printf("f1:b\n");
	}
	void f2(){
		printf("f2:b\n");
		f1();
	}
};

int main(){
	char a = ' ';
	A *b = new B();
	b->f1();
	b->f2();
	scanf("%c",a);

	return(0);
}


Output:
a
f1:a
f2:a
b
f1:b
f2:b
f1:b
f1:a
f2:b
f1:a

hope I didnt miss anything
Within class A, when a call to f1() or f2() is made, A::f1() and A::f2() are called respectively. This is because they are direct calls, not calls using the indirection of a reference or pointer.

The same goes for B.

In main(), where there is indirection, neither f1() or f2() are virtual, so A::f1() and A::f2() are called thru an A*.

Finally, don't mix stdio calls with C++ objects, you're just asking for trouble.
sorry, but I dont fully understand:
1:a
2:f1:a
3:f2:a
4:b
5:f1:b
6:f2:b
7:f1:b
8:f1:a
9:f2:b
10:f1:a

ln1: is because the parent const. is called first
ln2-3: I dont understand why its not f1:B or at least f2:B that shloud be called.
ln4:ok
ln5-7: ?
ln8: is because f1 is not virtual
ln9:is because f2 is virtual
ln10: accurding to what you say, this should be f1:b... I dont understand why here its different from ln7

thank you.
ln1: is because A's constructor is called by B's constructor before it runs.

ln2-3: f1() and f2() are called from A::A(). The virtual mechanism doesn't work in constructors.

ln5-6: f1() and f2() are called from B::B(). The virtual mechanism doesn't work in constructors.

ln7: f1() is called within B::f2() without indirection, so it looks for a B::f1() match first.
ln10: same as ln7

EDIT: Although C++ supports the Object Oriented paradigm, it also supports the Procedural Paradigm. The OO features only work thru explicit indirection.
Last edited on
First, thank you again.

Second, sorry for the ignorance but I still don't understand ln7 & 10:
1. you say its the same but the output isn't.
2. I don't understand what you mean by indirection? maybe that my problem. can you explain it please?

As I see it, and correct me if I'm wrong, in lns 1-7 the object is not yet "formed" and because of that the code dose not call this.f1() but just looks for the "nearest" f1(). in ln 8-10, the object has already been "formed" and thats why we call this.f1() for every call to a function, which leads to using the virtual mechanism. so thats why ln 7 and ln 10 differ.
Is that correct or I'm losing it?
No, I'm wrong. I'll look at again later today.
closed account (z05DSL3A)
tieger,
All I have just change the order in which the class and function names are displayed.
Do you get the same output with your compiler? If not post your output and compiler (make/version).
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 <stdio.h>

class A{
public:
    A(){
        printf("tA::n");
        f1();
        f2();
    }
    void f1(){
        printf("tA::f1()n");
    }
    virtual void f2(){
        printf("tA::f2()n");
    }
};

class B: public A {
public:
    B(){
        printf("tB::n");
        f1();
        f2();
    }
    void f1(){
        printf("tB::f1()n");
    }
    void f2(){
        printf("tB::f2()n");
        f1();
    }
};

int main(){
    char a = ' ';
    printf("Constructings a B::n");
    A *b = new B();
    printf("calling f1()...n");
    b->f1();
    printf("calling f2()...n");
    b->f2();
    scanf("%c",a);

    return(0);
}
Constructings a B::
        A::
        A::f1()
        A::f2()
        B::
        B::f1()
        B::f2()
        B::f1()
calling f1()...
        A::f1()
calling f2()...
        B::f2()
        B::f1()
Last edited on
@tieger

kbw's analysis is correct. The output is not correct and probably the compiler you use is not "correct". I get different output on my machine with mingw g . Line 10 must be f1:b. Aside from that, you should use "&a", not simply "a" in scanf, because the routine needs pointers to the variables that it has to fill.

You understand the suppression of the virtual mechanism during construction correctly.

Btw, I would really appreciate if someone could elaborate what the part I did put in bold in this snippet from the standard is supposed to mean:

Standard 2003 wrote:
and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor%u2019s own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor%u2019s class, or overriding it in one of the other base classes of the most derived object

I mean can we override virtual functions from sibling base classes?

Regards

P.S. It seems that I can not put g[plus][plus][period] here. The pluses are dropped after I submit the post. Is this possible?
Last edited on
@Grey Wolf - yes. I was wrong.
@kbw - sorry for misleading you.
ln10 is f1():b. my mistake.
now, can you explain please lns: 7, 10? what exactly is going there?
closed account (z05DSL3A)
If I understand you correctly.

When you call B::f2() it has a call to f1(), this call is effectively this->f1() or B::f1() so the output is f1:b.
Grey Wolf is right. This is my longer explanation. (I'm pedantic nerd type when it comes to this these matters.)

The language defines the concept of l-value and r-value. l-values corresponds to locations in memory that store data. r-values correspond to data not assigned to any specific location (although they will probably occupy some temporarily). l-values can be used in any context where r-values can be used, but not vice versa.

The primary sources of producing l-values are variables, references, and dereferencing of pointers. IIRC all other expressions (except calls) produce r-values. (Notice that whether a value allows modification depends on its type, not on its value category. r-value of assignable non-const type can be assigned to, while l-value of non-assignable or const type can not be assigned to. [edit]Fundamental built-in types excepted.[/edit])

(don't stop reading)

r-values have type that can be determined at compile-time. l-values on the other hand are more complex. If an l-value is some variable, its type is also known. But if an l-value is dereferenced pointer or reference, the actual type of the object is not known before running the program. This is because references and pointers can be up-casted for polymorphic usage.

The standard introduces two types - static and dynamic. The static type can be determined during compilation, while the dynamic is the actual type of the object, which may be different at run-time.

For an r-value the static and dynamic types are the same. For a variable, the static and dynamic types are also the same. The static type of l-value produced by dereferencing a pointer is specified in the pointer type. The dynamic type for pointer dereference can be different from the static, but taking the address of an object and dereferencing it later will recover the original dynamic type. The dynamic type for dynamically allocated objects is the one specified in the new expression. Similarly, references acquire the dynamic type of the object that you assign to them. Their static type is part of the reference type. Note that you can assign r-values (temporaries) to const references. So, technically, although less common, r-values may end up used polymorphically.

Now to polymorphism. If you call a non-virtual method, the implementation corresponding to the static type is used. If you call virtual method during construction, again the static type determines the implementation that will be used. If you call virtual method after the construction is completed, the dynamic type is used.

That's why b->f1() results in call to A::f1(). This is the same as (*b).f1(), which means that you call method for an l-value. The l-value pointed by b has static and dynamic type. The dynamic type is B and the static type is A. But since this is non-virtual function, the static type is used anyway. For the call b->f2(), which is equivalent to (*b).f2(), the dynamic type is used, i.e. B::f2() is called. In fact B::f2() is not called directly, because there is a small prologue code that converts the pointer from A* to B* and assigns the result to the this pointer.

When in B::f2, a call to f1 again is made. But f1() is equivalent to this->f1() is equivalent to (*this).f1(). The l-value *this again has static and dynamic type. Both are B. Specifically, as I already said, during the f2 call the pointer was converted to B* before assigning it to this and that's why the static type of *this is B. As always the call to f1 is resolved according to the static type of the l-value and consequently B::f1 is called.

Regards
Last edited on
@simeonz - thanks!

Is it safe to say that for every call to a func within a class, the func that will be called is the one thats in the class? I mean, the virtual mechanism and the deriviation mechnisim works only from the outside. when you are in a func and there is a call to another func from there, it goes to the nearest one.
it that correct?
Polymorphism will still take effect, because when you call on this you are using a pointer, so you call the function of the actual class pointed to by this.

simeonz wrote:
If you call virtual method after the construction is completed, the dynamic type is used.
Zhuge is right.

Whenever a call like method() is encountered in class scope (assuming such method exists), it is actually converted to this->method(), which is equivalent to (*this).method(). You are calling on the *this l-value and its dynamic and static types may differ. Particularly, the caller may be implemented in some base class, while the callee may be implemented (or more precisely finally overridden) in some derived class. The callee may even be not implemented (pure virtual) in the abstract class of the caller.
I think I got it.
Thanks
Topic archived. No new replies allowed.