Index | Prev | Next

C & C++ Programming

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.

I’ve 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 that’s 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:

Index | Prev | Next