Accessing value using get method or member directly? - c++

class SomeClass
{
int classMember;
public:
int GetMember();
bool IsPositive();
};
int SomeClass::GetMember()
{
return classMember;
}
bool SomeClass::IsPositive()
{
int val = GetMember(); //Case#1. Accessing value using get method
int val = classMember; //Case#2. Accessing value directly
return (val > 0);
}
Question: Does using Case#1 have any advantages over Case#2 or vice versa? Are there any overheads (even the tiniest) in using get method as compared to the member directly?

The answer is, it really depends on how your compiler chooses to do something. The best way to see if there is a difference is to look at the disassembly.
int val = classMember;
010C4869 mov eax,dword ptr [this]
010C486C mov ecx,dword ptr [eax]
010C486E mov dword ptr [val],ecx
return val > 0;
010C4871 cmp dword ptr [val],0
010C4875 jle SomeClass::IsPositiveClassMember+20h (010C4880h)
010C4877 mov dword ptr [ebp-4],1
010C487E jmp SomeClass::IsPositiveClassMember+27h (010C4887h)
010C4880 mov dword ptr [ebp-4],0
010C4887 mov al,byte ptr [ebp-4]
vs.
int val = GetMember();
010C4829 mov ecx,dword ptr [this]
010C482C call SomeClass::GetMember (010C1168h)
010C4831 mov dword ptr [val],eax
return val > 0;
010C4834 cmp dword ptr [val],0
010C4838 jle SomeClass::IsPositiveGetMember+23h (010C4843h)
010C483A mov dword ptr [ebp-4],1
010C4841 jmp SomeClass::IsPositiveGetMember+2Ah (010C484Ah)
010C4843 mov dword ptr [ebp-4],0
010C484A mov al,byte ptr [ebp-4]
The second example calls SomeClass::GetMember, which has its own disassembly. So in the second case, instead of just loading the value from member, it makes a function call.
return classMember;
010C4817 mov eax,dword ptr [this]
010C481A mov eax,dword ptr [eax]
You'll note that the instructions to load val with the value of classMember are identical, so the overhead comes from the Call SomeClass::GetMember.
This is in debug mode however, with no optimization. If we optimize, and build in release, we see the following disassembly:
int val = classMember;
return val > 0;
013D4830 xor eax,eax
013D4832 cmp dword ptr [ecx],eax
013D4834 setg al
vs
int val = GetMember();
return val > 0;
013D4820 xor eax,eax
013D4822 cmp dword ptr [ecx],eax
013D4824 setg al
The compiler optimizes away the call, and there is no difference.

Its purely depend on the need of programmer.
When to create function?
To increase the modularity of the program
When some common tasks are performed repeatedly.
In your case say suppose two number to be added.
For that you may use
int a= data member 1 + data member 2
Say suppose you have to use that at many places.
Then you have consider the point number 2.
You can just create the function like addnumber() this for ease of use and readability.
Regarding performance,
Both are same because the member function are inline by default when defined inside class definition. So separate stack allocation for calling that function is not required

Using GetMember() might be a bit slower, unless its made inline explicitly or implicitly by your compiler.
However, using an accessor can greatly help your debugging, temporarily changing this:
int SomeClass::GetMember()
{
return classMember;
}
into this:
int SomeClass::GetMember()
{
std::cout << "GetMember() called when classMember=" << classMember << std::endl;
return classMember;
}
But this might be a bit oldschool.

Related

Do modern compilers optimize local variables that just refer to subset of objects?

Do most modern compilers end up optimizing the following code so that extra instructions aren't used for the object inner?
func Test(TypeObject *object):
InnerTypedObject *inner = object->inner
print(inner->a)
print(inner->b)
print(inner->c)
I figured that compilers would be able to figure out that inner->a and object->inner.a refer to the same thing, so it would avoid allocating inner altogether. I figured the local variable is probably saved on a register, so I'm not really concerned about performance. Mainly wanted to know if we'd get the same generated machine code.
Thanks to Jerry Coffin for the comment - my original answer was actually quite wrong...
For this code:
struct TypeObject {
int a;
int b;
int c;
};
void print(int x);
void test(TypeObject *object) {
print(object->a);
print(object->b);
print(object->c);
}
https://godbolt.org/g/SrNWkp produces something like this:
test(TypeObject*):
push rbx // save the rbx register
mov rbx, rdi // copy the parameter (which is "object") to rbx
mov edi, DWORD PTR [rbx] // copy inner->a to edi
call print(int)
mov edi, DWORD PTR [rbx+4] // copy inner->b to edi
call print(int)
mov edi, DWORD PTR [rbx+8] // copy inner->c to edi
jmp print(int)
pop rbx // restore rbx
And for this code:
struct InnerTypedObject {
int a;
int b;
int c;
};
struct TypeObject {
InnerTypedObject * inner;
};
void print(int x);
void test(TypeObject *object) {
InnerTypedObject *inner = object->inner;
print(inner->a);
print(inner->b);
print(inner->c);
}
https://godbolt.org/g/NC2pa3 produces something like this:
test(TypeObject*):
push rbx // save the rbx register
mov rbx, QWORD PTR [rdi] // copy "*object" (which is "inner") to rbx
mov edi, DWORD PTR [rbx] // copy inner->a to edi
call print(int)
mov edi, DWORD PTR [rbx+4] // copy inner->b to edi
call print(int)
mov edi, DWORD PTR [rbx+8] // copy inner->c to edi
jmp print(int)
pop rbx // restore rbx
So the code is still dereferencing object - it stores the pointer once and then uses it three times just like the original code did. The reason for not being able to optimize it better is that what is stored in a pointer is extremely hard to track so the optimizer has to assume it doesn't know what is in there for sure.
Even though both bits of assembly have the same number of instructions, there is an extra memory dereference in the one with "inner" so it could be expensive if the data isn't already in the cache.

why constructors "return" this pointer?

I noticed that the constructor will move this to eax before returning. This is a return value or something else?
class CTest {
int val_;
public:
CTest() {
0093F700 push ebp
0093F701 mov ebp,esp
0093F703 sub esp,0CCh
0093F709 push ebx
0093F70A push esi
0093F70B push edi
0093F70C push ecx
0093F70D lea edi,[ebp-0CCh]
0093F713 mov ecx,33h
0093F718 mov eax,0CCCCCCCCh
0093F71D rep stos dword ptr es:[edi]
0093F71F pop ecx
0093F720 mov dword ptr [this],ecx
val_ = 1;
0093F723 mov eax,dword ptr [this]
0093F726 mov dword ptr [eax],1
}
0093F72C mov eax,dword ptr [this]
0093F72F pop edi
0093F730 pop esi
0093F731 pop ebx
0093F732 mov esp,ebp
0093F734 pop ebp
0093F735 ret
VS2012 debug mode
I found that new will use its "return value". Seems like if(operator new() == 0) return 0; else return constructor();
class CTest {
int val_;
public:
CTest() {
val_ = 1;
__asm {
mov eax, 0x12345678
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
}
}
};
int main() {
CTest *test = new CTest; // test == 0x12345678
return 0;
}
Your second question disagrees with your first. How can new use if ( operator new() == 0 ) return 0; else return constructor(); if constructor() is producing the condition result?
Anyway…
What the compiler does with registers is the compiler's business. Registers tend to hold whatever information is immediately useful, and if the compiler is written with the belief that every time the constructor is used, the object is used immediately afterwards, it may reasonably choose to put this in a register.
An ABI may require constructors to do this, but I doubt any do. Anyway, such protocols only apply to things exported from libraries, not strictly within programs.
Any new expression does check the result of operator new against 0 before proceeding to initialize an object. operator new may signal failure by returning nullptr (or NULL, etc.).
This can actually be a problem with placement new expressions, because it represents unavoidable runtime overhead as the given pointer is generally already known to be non-null.
This can be a feature by design, in C++ and other languages, returning a reference to a given instance allows a more "idiomatic" use of the features offered by the object itself, in short it's the Named parameter Idiom .
But this is just 1 option, it can be useful sometimes, especially if you are able to design your library in a way that it only "takes actions" without having the need to pass a significant amount of parameters, so the chain of method calls stays readable.

Preprocessor macro with dot?

Is it possible to define a macro that would accept expression like: object.method()? I would like to make a macro that changes that expression into... nothing (kind of deletes it). With just function() I would do: #define function (without any value) but is it possible to create a macro with dot in it?
Edit: Regarding MooingDuck's comment:
object.Method("text", "other");
Definition:
void Class::Method(std::string arg1, std::string arg2)
{
#if 0
if (condition)
{
Method2(arg1, arg2);
}
#endif
}
Disassembly:
object.Method("text", "other");
00394396 mov edi,5
0039439B mov eax,offset string "other" (396348h)
003943A0 lea esi,[ebp-4Ch]
003943A3 mov dword ptr [ebp-38h],0Fh
003943AA mov dword ptr [ebp-3Ch],ebx
003943AD mov byte ptr [ebp-4Ch],bl
003943B0 call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign (393360h)
003943B5 mov dword ptr [ebp-4],1
003943BC mov edi,4
003943C1 mov eax,offset string "text" (396350h)
003943C6 lea esi,[ebp-30h]
003943C9 mov dword ptr [ebp-1Ch],0Fh
003943D0 mov dword ptr [ebp-20h],ebx
003943D3 mov byte ptr [ebp-30h],bl
003943D6 call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign (393360h)
003943DB mov esi,10h
003943E0 mov dword ptr [ebp-4],0FFFFFFFFh
003943E7 cmp dword ptr [ebp-1Ch],esi
003943EA jb main+0B9h (3943F9h)
003943EC mov eax,dword ptr [ebp-30h]
003943EF push eax
003943F0 call dword ptr [__imp_operator delete (3960ECh)]
003943F6 add esp,4
003943F9 mov edi,0Fh
003943FE mov dword ptr [ebp-1Ch],edi
00394401 mov dword ptr [ebp-20h],ebx
00394404 mov byte ptr [ebp-30h],bl
00394407 cmp dword ptr [ebp-38h],esi
0039440A jb main+0D9h (394419h)
0039440C mov ecx,dword ptr [ebp-4Ch]
0039440F push ecx
00394410 call dword ptr [__imp_operator delete (3960ECh)]
00394416 add esp,4
Your code is:
void Class::Method(const std::string& arg1, const std::string& arg2)
{
#if 0
if (condition)
{
Method2(arg1, arg2);
}
#endif
}
But the problem is that when you call this with string literals, it's still constructing the std::string objects because the process of constructing them might have side effects.
I think I would try this, which won't do conversions until inside of the function, and thus the compiler can elide the std::string constructions as well.
template<class arg1_t, class arg2_t>
void Class::Method(const arg1_t& arg1, const arg2_t& arg2)
{
#if 0
if (condition)
{
Method2(arg1, arg2);
}
#endif
}
The name of the macro is an identifier. There is no way around.
Different compilers may allow different characters into identifier (like $), but none of them allow .. At the end of the day C-preprocessor was designed to be a "light preprocessor", not intended for fancy conversions like other, much more powerful preprocessors, that were existing in mid 80's.
Although you can do something like this (not really recommended):
struct DoNothing
{
void method() { }
};
DoNothing g_inst;
#define object g_inst
Almost certainly the macro generated code will be completely removed by the optimizer. This approach is sensitive to all used names. Everything should be explicitly mentioned.

function calls during a standard c++ program execution

I have the following code:
int func(int a)
{
int b=2;
int c,d,e,f,g;
//some code which involves a,b,c,d,e,f,g
}
int main()
{
int s=3;
func(s);
}
Now what happens is that when main begins execution:
1.It pushes s onto stack
2.It calls func()
3.func() pushes b,c,d,e,f,g onto stack
4.Now when the code involving a,b,c,d,e,f.g is executed, in order to know the value of a all local variables of func() will have to be popped. Then a's value is retrieved. Now if again b.c.d.e.f.g are to be used, how will their values be retrieved (because they have already been popped)?
The local variables, as well as the argument, aren't actually pushed on the stack. Instead the compiler adds code to change the stack pointer by enough to fit all variables, and then when referencing a local variable the compiler have code to get the value from an offset from the stack pointer.
I recommend that you look at the assembler output of your example program to understand how it works.
The equivalent code for void func(int a)
void func(int a)
{
00413880 push ebp
00413881 mov ebp,esp
00413883 sub esp,108h
00413889 push ebx
0041388A push esi
0041388B push edi
0041388C lea edi,[ebp-108h]
00413892 mov ecx,42h
00413897 mov eax,0CCCCCCCCh
0041389C rep stos dword ptr es:[edi]
int b=2;
0041389E mov dword ptr [b],2
int c,d,e,f,g;
//some code which involves a,b,c,d,e,f,g
}
Now lets see the equivalent assembly code for the below code::
void func(int a)
{
int b=2;
int c,d,e,f,g;
c = 10 ;
d = 15 ;
e = 20 ;
a = a + 2 ;
}
Assembly Code::
void func(int a)
{
00413880 push ebp
00413881 mov ebp,esp
00413883 sub esp,108h
00413889 push ebx
0041388A push esi
0041388B push edi
0041388C lea edi,[ebp-108h]
00413892 mov ecx,42h
00413897 mov eax,0CCCCCCCCh
0041389C rep stos dword ptr es:[edi]
int b=2;
0041389E mov dword ptr [b],2
int c,d,e,f,g;
c = 10 ;
004138A5 mov dword ptr [c],0Ah
d = 15 ;
004138AC mov dword ptr [d],0Fh
e = 20 ;
004138B3 mov dword ptr [e],14h
a = a + 2 ;
004138BA mov eax,dword ptr [a]
004138BD add eax,2
004138C0 mov dword ptr [a],eax
}
So, although they are pushed in Stack (ESP or SP) but the pointer to each of the local variables are stored too, so that, they can be accessed easily when needed.
Like, when the code needed to use variable a, the dword ptr [a] is simply moved to register EAX.
NOTE: Technically not pushed but adjusted to fit in all variables. ( Courtesy: Joachim Pileborg)
in order to know the value of a all local variables of func() will have to be popped
This line is although grammatically ambiguous. I ll assume you mean to say the varibles are popped when being used by function..
But the actual case is local variable are popped out only when function returns to the caller.
Provided they are automatic.
On the other hand when function wants to access(read/write) the variable. It uses the offset(distance) from the base to access them, so there is no question of variables being popped out while being accessed for evaluation.

How does returning values from a function work?

I recently had a serious bug, where I forgot to return a value in a function. The problem was that even though nothing was returned it worked fine under Linux/Windows and only crashed under Mac. I discovered the bug when I turned on all compiler warnings.
So here is a simple example:
#include <iostream>
class A{
public:
A(int p1, int p2, int p3): v1(p1), v2(p2), v3(p3)
{
}
int v1;
int v2;
int v3;
};
A* getA(){
A* p = new A(1,2,3);
// return p;
}
int main(){
A* a = getA();
std::cerr << "A: v1=" << a->v1 << " v2=" << a->v2 << " v3=" << a->v3 << std::endl;
return 0;
}
My question is how can this work under Linux/Windows without crashing? How is the returning of values done on lower level?
On Intel architecture, simple values (integers and pointers) are usually returned in eax register. This register (among others) is also used as temporary storage when moving values in memory and as operand during calculations. So whatever value left in that register is treated as the return value, and in your case it turned out to be exactly what you wanted to be returned.
Probably by luck, 'a' left in a register that happens to be used for returning single pointer results, something like that.
The calling/ conventions and function result returns are architecture-dependent, so it's not surprising that your code works on Windows/Linux but not on a Mac.
There are two major ways for a compiler to return a value:
Put a value in a register before returning, and
Have the caller pass a block of stack memory for the return value, and write the value into that block [more info]
The #1 is usually used with anything that fits into a register; #2 is for everything else (large structs, arrays, et cetera).
In your case, the compiler uses #1 both for the return of new and for the return of your function. On Linux and Windows, the compiler did not perform any value-distorting operations on the register with the returned value between writing it into the pointer variable and returning from your function; on Mac, it did. Hence the difference in the results that you see: in the first case, the left-over value in the return register happened to co-inside with the value that you wanted to return anyway.
First off, you need to slightly modify your example to get it to compile. The function must have at least an execution path that returns a value.
A* getA(){
if(false)
return NULL;
A* p = new A(1,2,3);
// return p;
}
Second, it's obviously undefined behavior, which means anything can happen, but I guess this answer won't satisfy you.
Third, in Windows it works in Debug mode, but if you compile under Release, it doesn't.
The following is compiled under Debug:
A* p = new A(1,2,3);
00021535 push 0Ch
00021537 call operator new (211FEh)
0002153C add esp,4
0002153F mov dword ptr [ebp-0E0h],eax
00021545 mov dword ptr [ebp-4],0
0002154C cmp dword ptr [ebp-0E0h],0
00021553 je getA+7Eh (2156Eh)
00021555 push 3
00021557 push 2
00021559 push 1
0002155B mov ecx,dword ptr [ebp-0E0h]
00021561 call A::A (21271h)
00021566 mov dword ptr [ebp-0F4h],eax
0002156C jmp getA+88h (21578h)
0002156E mov dword ptr [ebp-0F4h],0
00021578 mov eax,dword ptr [ebp-0F4h]
0002157E mov dword ptr [ebp-0ECh],eax
00021584 mov dword ptr [ebp-4],0FFFFFFFFh
0002158B mov ecx,dword ptr [ebp-0ECh]
00021591 mov dword ptr [ebp-14h],ecx
The second instruction, the call to operator new, moves into eax the pointer to the newly created instance.
A* a = getA();
0010484E call getA (1012ADh)
00104853 mov dword ptr [a],eax
The calling context expects eax to contain the returned value, but it does not, it contains the last pointer allocated by new, which is incidentally, p.
So that's why it works.
As Kerrek SB mentioned, your code has ventured into the realm of undefined behavior.
Basically, your code is going to compile down to assembly. In assembly, there's no concept of a function requiring a return type, there's just an expectation. I'm the most comfortable with MIPS, so I shall use MIPS to illustrate.
Assume you have the following code:
int add(x, y)
{
return x + y;
}
This is going to be translated to something like:
add:
add $v0, $a0, $a1 #add $a0 and $a1 and store it in $v0
jr $ra #jump back to where ever this code was jumped to from
To add 5 and 4, the code would be called something like:
addi $a0, $0, 5 # 5 is the first param
addi $a1, $0, 4 # 4 is the second param
jal add
# $v0 now contains 9
Note that unlike C, there's no explicit requirement that $v0 contain the return value, just an expectation. So, what happens if you don't actually push anything into $v0? Well, $v0 always has some value, so the value will be whatever it last was.
Note: This post makes some simplifications. Also, you're computer is likely not running MIPS... But hopefully the example holds, and if you learned assembly at a university, MIPS might be what you know anyway.
The way of returning of value from the function depends on architecture and the type of value. It could be done thru registers or thru stack.
Typically in the x86 architecture the value is returned in EAX register if it is an integral type: char, int or pointer.
When you don't specify the return value, that value is undefined. This is only your luck that your code sometimes worked correctly.
When popping values from the stack in IBM PC architecture there is no physical destruction of the old values ​​of data stored there. They just become unavailable through the operation of the stack, but still remain in the same memory cell.
Of course, the previous values ​​of these data will be destroyed during the subsequent pushing of new data on the stack.
So probably you are just lucky enough, and nothing is added to stack during your function's call and return surrounding code.
Regarding the following statement from n3242 draft C++ Standard, paragraph 6.6.3.2, your example yields undefined behavior:
Flowing off the end of a function is equivalent to a return with no
value; this results in undefined behavior in a value-returning
function.
The best way to see what actually happens is to check the assembly code generated by the given compiler on a given architecture. For the following code:
#pragma warning(default:4716)
int foo(int a, int b)
{
int c = a + b;
}
int main()
{
int n = foo(1, 2);
}
...VS2010 compiler (in Debug mode, on Intel 32-bit machine) generates the following assembly:
#pragma warning(default:4716)
int foo(int a, int b)
{
011C1490 push ebp
011C1491 mov ebp,esp
011C1493 sub esp,0CCh
011C1499 push ebx
011C149A push esi
011C149B push edi
011C149C lea edi,[ebp-0CCh]
011C14A2 mov ecx,33h
011C14A7 mov eax,0CCCCCCCCh
011C14AC rep stos dword ptr es:[edi]
int c = a + b;
011C14AE mov eax,dword ptr [a]
011C14B1 add eax,dword ptr [b]
011C14B4 mov dword ptr [c],eax
}
...
int main()
{
011C14D0 push ebp
011C14D1 mov ebp,esp
011C14D3 sub esp,0CCh
011C14D9 push ebx
011C14DA push esi
011C14DB push edi
011C14DC lea edi,[ebp-0CCh]
011C14E2 mov ecx,33h
011C14E7 mov eax,0CCCCCCCCh
011C14EC rep stos dword ptr es:[edi]
int n = foo(1, 2);
011C14EE push 2
011C14F0 push 1
011C14F2 call foo (11C1122h)
011C14F7 add esp,8
011C14FA mov dword ptr [n],eax
}
The result of addition operation in foo() is stored in eax register (accumulator) and its content is used as a return value of the function, moved to variable n.
eax is used to store a return value (pointer) in the following example as well:
#pragma warning(default:4716)
int* foo(int a)
{
int* p = new int(a);
}
int main()
{
int* pn = foo(1);
if(pn)
{
int n = *pn;
delete pn;
}
}
Assembly code:
#pragma warning(default:4716)
int* foo(int a)
{
000C1520 push ebp
000C1521 mov ebp,esp
000C1523 sub esp,0DCh
000C1529 push ebx
000C152A push esi
000C152B push edi
000C152C lea edi,[ebp-0DCh]
000C1532 mov ecx,37h
000C1537 mov eax,0CCCCCCCCh
000C153C rep stos dword ptr es:[edi]
int* p = new int(a);
000C153E push 4
000C1540 call operator new (0C1253h)
000C1545 add esp,4
000C1548 mov dword ptr [ebp-0D4h],eax
000C154E cmp dword ptr [ebp-0D4h],0
000C1555 je foo+50h (0C1570h)
000C1557 mov eax,dword ptr [ebp-0D4h]
000C155D mov ecx,dword ptr [a]
000C1560 mov dword ptr [eax],ecx
000C1562 mov edx,dword ptr [ebp-0D4h]
000C1568 mov dword ptr [ebp-0DCh],edx
000C156E jmp foo+5Ah (0C157Ah)
std::operator<<<std::char_traits<char> >:
000C1570 mov dword ptr [ebp-0DCh],0
000C157A mov eax,dword ptr [ebp-0DCh]
000C1580 mov dword ptr [p],eax
}
...
int main()
{
000C1610 push ebp
000C1611 mov ebp,esp
000C1613 sub esp,0E4h
000C1619 push ebx
000C161A push esi
000C161B push edi
000C161C lea edi,[ebp-0E4h]
000C1622 mov ecx,39h
000C1627 mov eax,0CCCCCCCCh
000C162C rep stos dword ptr es:[edi]
int* pn = foo(1);
000C162E push 1
000C1630 call foo (0C124Eh)
000C1635 add esp,4
000C1638 mov dword ptr [pn],eax
if(pn)
000C163B cmp dword ptr [pn],0
000C163F je main+51h (0C1661h)
{
int n = *pn;
000C1641 mov eax,dword ptr [pn]
000C1644 mov ecx,dword ptr [eax]
000C1646 mov dword ptr [n],ecx
delete pn;
000C1649 mov eax,dword ptr [pn]
000C164C mov dword ptr [ebp-0E0h],eax
000C1652 mov ecx,dword ptr [ebp-0E0h]
000C1658 push ecx
000C1659 call operator delete (0C1249h)
000C165E add esp,4
}
}
VS2010 compiler issues warning 4716 in both examples. By default this warning is promoted to an error.