Which is the parameter that is added to every non-static member function when it
is called?
'this' pointer
class CBase
{
public:
int bval;
CBase(){ bval = 0;}
};
class CDerived : public CBase
{
public:
int dval;
CDerived(){ dval=1;}
};
void MyFunc(CBase *arr, int size)
{
for(int i=0; i‹size; i++,arr++) {
cout«arr-›bval; cout«endl;
}
int main()
{
CBase BaseArr[5];
MyFunc(BaseArr, 5);
CDerived DeriArr[5];
MyFunc(DeriArr, 5);
}
00000
01010
The function MyFunc expects two arguments. The first one is a pointer to an array
of CBase class objects and the second one is the sizeof the array. The first call
of MyFunc calls it with an array of base objects, so it works correctly and prints
the bval of all the objects. When MyFunc is called the second time the argument
passed is the pointer to an array of derived class objects and not the array of
CBase class objects. But that is what the function expects to be sent. So the derived
class pointer is promoted to CBase class pointer and the address is sent to the
function. MyFunc() does not knows about this and just treats the pointer as an array
of CBase class objects. So when arr++ is met, the size of CBase class object is
taken into consideration and is incremented by sizeof(int) bytes for bval (the CDerived
class objects have bval and dval as members and so is of size ›= sizeof(int)+sizeof(int)
).
Calling Non-virtual function
class CBase
{
public:
CBase()
{
cout << "Base CTOR" << endl;
}
void Func()
{
cout << "Base" << endl;
}
};
class CDerived : public CBase
{
public:
int dval;
CDerived()
{
cout << "Derived CTOR" << endl;
}
void Func()
{
cout << "Derived" << endl;
}
};
void SomeFunc(CBase* baseObj)
{
baseObj->Func();
}
CBase baseObject;
SomeFunc(&baseObject);
CDerived deriObject;
SomeFunc(&deriObject);
Since a pointer to a derived class object is passed, it treats the argument only
as a base class pointer and the corresponding base function is called.
To provide supports run-time polymorphism. Define the Func as virtual function so
the corresponding derived class func is called.
Q: What are the advantages of inline functions?
A: Inline functions are best used for small functions such as accessing private
data members. The main purpose of these inline functions (usually one or two lines
of code) is to return state information about objects; short functions are sensitive
to the overhead of function calls. Longer functions spend proportionately less time
in the calling/returning sequence and benefit less from inlining. An inline function
typically saves the overhead of:
- Function calls (including parameter passing and placing the object's address on
the stack)
- Preservation of caller's stack frame
- New stack-frame setup
- Return-value communication
- Old stack-frame restore
- Return
The
inline and
__inline specifiers instruct the compiler to insert
a copy of the function body into each place the function is called. The insertion
(called "inline expansion" or "inlining") occurs only if the compiler's cost/benefit
analysis show it to be profitable. Inline expansion alleviates the function-call
overhead at the potential cost of larger code size.
Because there is no guarantee that an inline function will actually be inlined,
you can bypass the compiler optimization by using the
__forceinline keyword.
This is Microsoft specific.
Quote:
Originally Posted by MSDN
The __forceinline keyword overrides the cost/benefit analysis and relies on the
judgment of the programmer instead. Exercise caution when using __forceinline. Indiscriminate
use of __forceinline can result in larger code with only marginal performance gains
or, in some cases, even performance losses (due to increased paging of a larger
executable, for example).
|
However, this still does not force the compiler to actually inline every function
you declared that way...
Quote:
Originally Posted by MSDN
The compiler treats the inline expansion options and keywords as suggestions. There
is no guarantee that functions will be inlined. You cannot force the compiler to
inline a particular function, even with the __forceinline keyword.
|
For further information, please refer to the
MSDN.
In C++ a class's member functions can be declared inline either by using the
inline
keyword or by placing the function definition within the class definition.
Code:
class foo
{
int a;
public:
int getA() {return a;} // implicitly inline
};
Q: What is the difference between inline functions and macros.
A: Inline functions are similar to macros because they both are expanded
at compile time, but the macros are expanded by the preprocessor, while inline functions
are parsed by the compiler. There are several important differences:
- Inline functions follow all the protocols of type safety enforced on normal functions.
- Inline functions are specified using the same syntax as any other function except
that they include the inline keyword in the function declaration.
- Expressions passed as arguments to inline functions are evaluated once. In some
cases, expressions passed as arguments to macros can be evaluated more than once.
Q: When should the macro used and when inline functions?
A: Besides the difference already pointed out, you also must have in mind
that because macros are expanded at pre-compile time, you cannot use them for debugging,
but you can use inline functions.
Code:
#define max(a,b) (a>b?a:b)
class foo
{
public:
inline int maxim(int a, int b);
};
inline int foo::maxim(int a, int b)
{
return a > b ? a : b;
}
int main()
{
foo f;
int x = max(1,2);
int y = f.maxim(1,2);
return 0;
}
In this example you can put a breakpoint in
foo::maxim and step into the
method, though it is an inline function. (if
maxim was defined in the body
of the
foo class the keyword
inline would have not been necessary)
However, because macros are expanded before compilation actually starts, you cannot
do that with the macro
max. It is expanded at pre-compile time to:
Code:
int main()
{
foo f;
int x = 1>2?1:2;
int y = f.maxim(1,2);
return 0;
}
Also you must be carefully with macros, because they can have the arguments evaluated
more than once. Here is an example:
Code:
#include <iostream>
using namespace std;
#define max(a,b) (a>b?a:b)
int main()
{
int a = 0;
int b = 1;
int c = max(a++, b++);
cout << a << endl << b << endl;
return 0;
}
The intention is to have the program print 1 and 2, but because the macro is expanded
to:
Code:
int c = a++ > b++ ? a++ : b++;
b will be incremented twice, and the program prints 1 and 3.
Ive seen several ugly ways of using macros. The example below synthesizes the idea
of bad macro usage:
Code:
#define INIT \
if(pSomeData == NULL) { \
pSomeData = new int; \
*pSomeData = 0; \
} else { \
*pSomeData = 0; \
}
class foo
{
int* pSomeData;
public:
foo();
void do_something();
};
foo::foo()
{
INIT
// do other things
}
void foo::do_something()
{
INIT
// do something here
}
Never do something like that. You can put the initialization in a member function
of foo.
Here is just another bad example of macros:
Code:
#include <iostream>
using namespace std;
#define BEGIN \
if(index<0 || index>=5) \
return; \
goo temp = _goo[index]; \
temp.Init(); \
#define END \
temp.Close();
class goo
{
public:
goo(){}
goo(const goo& cpy) {}
const goo& operator=(const goo& rval) { return *this;}
void Init() { cout << "goo::init" << endl;}
void Action() {cout << "goo::action" << endl;}
void Reaction() {cout << "goo::reaction" << endl;}
void Close() {cout << "goo::close" << endl;}
};
class foo
{
goo *_goo;
public:
foo();
~foo();
void do_something(int index);
void do_something_else(int index);
};
foo::foo()
{
_goo = new goo[5];
}
foo::~foo()
{
delete [] _goo;
}
void foo::do_something(int index)
{
BEGIN
temp.Action();
END
}
void foo::do_something_else(int index)
{
BEGIN
temp.Reaction();
END
}
int main()
{
foo f;
f.do_something(0);
f.do_something_else(4);
return 0;
}
If you have something like this in your code then thats a sign of bad design and
must use factorization.
Q: From what I've seen so far, I should be cautious with macros. Aren't macros
useful after all?
A: There are many useful macros. There are predefined macros, some ANSI compliant,
some Microsoft specific. Here are ANSI-Compliant Predefined Macros:
- __DATE__ : The compilation date of the current source file.
- __FILE__ : The name of the current source file.
- __LINE__ : The line number in the current source file.
- __STDC__ : Indicates full conformance with the ANSI C standard.
- __TIME__ : The most recent compilation time of the current source file.
- __TIMESTAMP__ : The date and time of the last modification of the current source
file, expressed as a string literal.
Read more about these predefined macros in
MSDN.
Very useful macros are ASSERT and TRACE (which comes in several formes). Read this
FAQ about how to use ASSERT. If you look how it is defined in afx.h:
Code:
#define TRACE ::AfxTrace
#define THIS_FILE __FILE__
#define ASSERT(f) \
do \
{ \
if (!(f) && AfxAssertFailedLine(THIS_FILE, __LINE__)) \
AfxDebugBreak(); \
} while (0) \
you can see that it uses __FILE__ and __LINE__ to indicate the source file and the
line where the assertion failed.
The other macro that I've mention, TRACE, sends the specified string to the debugger
of the current application (just as ATLTRACE2 which has the same behaviour).
By mentioning just these macros doesn't mean they are the only useful macros. They
are just some examples of useful macros. You should use macros when you cannot do
something with an inline function or where the code will be more clean (maintainable)
with the use of macros.
Q: Where can I read more about macros and inline functions?
A: Here are some links: