OOPS Interview Questions
OOPS Interview Questions
OOPS Interview Questions
---------
Q: Write a short code using C++ to print out all odd number from 1 to 100 using a
for loop(Asked by Intacct.com people)
ISO layers and what layer is the IP operated from?( Asked by Cisco system people)
cation, Presentation, Session, Transport, Network, Data link and Physical. The IP is operated in
the Network layer.
3.Q: Write a program that ask for user input from 5 to 9 then calculate the
average( Asked by Cisco system people)
A.int main()
{
int MAX=4;
int total =0;
int average=0;
int numb;
cout<<"Please enter your input from 5 to 9";
cin>>numb;
if((numb <5)&&(numb>9))
cout<<"please re type your input";
else
for(i=0;i<=MAX; i++)
{
total = total + numb;
average= total /MAX;
}
cout<<"The average number is"<<average<<endl;
return 0;
}
4.Q: Can you be bale to identify between Straight- through and Cross- over cable
wiring? and in what case do you use Straight- through and Cross-over? (Asked by
Cisco system people)
A. Straight-through is type of wiring that is one to to one connection Cross- over is type of
wiring which those wires are got switched
We use Straight-through cable when we connect between NIC Adapter and Hub. Using Cross-
over cable when connect between two NIC Adapters or sometime between two hubs.
5.Q: If you hear the CPU fan is running and the monitor power is still on, but you did
not see any thing show up in the monitor screen. What would you do to find out
what is going wrong? (Asked by WNI people)
A. I would use the ping command to check whether the machine is still alive(connect to the
network) or it is dead.
^Back to Top
void reverselist(void)
{
if(head==0)
return;
if(head->next==0)
return;
if(head->next==tail)
{
head->next = 0;
tail->next = head;
}
else
{
node* pre = head;
node* cur = head->next;
node* curnext = cur->next;
head->next = 0;
cur->next = head;
for(; curnext!=0; )
{
cur->next = pre;
pre = cur;
cur = curnext;
curnext = curnext->next;
}
curnext->next = cur;
}
}
2. What is polymorphism?
Polymorphism is the idea that a base class can be inherited by several classes. A base class
pointer can point to its child class and a base class array can store different child class objects.
3. How do you find out if a linked-list has an end? (i.e. the list is not a cycle)
You can find out by using 2 pointers. One of them goes 2 nodes each time. The second one
goes at 1 nodes each time. If there is a cycle, the one that goes 2 nodes each time will
eventually meet the one that goes slower. If that is the case, then you will know the linked-list
is a cycle.
4. How can you tell what shell you are running on UNIX system?
You can do the Echo $RANDOM. It will return a undefined variable if you are from the C-Shell,
just a return prompt if you are from the Bourne shell, and a 5 digit random numbers if you are
from the Korn shell. You could also do a ps -l and look for the shell with the highest PID.
A relation schema R is in BCNF with respect to a set F of functional dependencies if for all
functional dependencies in F+ of the form a->b, where a and b is a subset of R, at least one of
the following holds:
• a->b is a trivial functional dependency (b is a subset of a)
• a is a superkey for schema R
^Back to Top
A reader submitted the interview questions he was asked. More C/C++ questions will be
added here, as people keep sending us a set of 2-3 questions they got on their job itnerview.
A: Create two pointers, each set to the start of the list. Update each as follows:
while (pointer1) {
pointer1 = pointer1->next;
pointer2 = pointer2->next; if (pointer2) pointer2=pointer2->next;
if (pointer1 == pointer2) {
print (\"circular\n\");
}
}
Q2: OK, why does this work?
If a list is circular, at some point pointer2 will wrap around and be either at the item just
before pointer1, or the item before that. Either way, it’s either 1 or 2 jumps until they meet.
How can you quickly find the number of elements stored in a a) static array b) dynamic
array ?
How can you find the nodes with repetetive data in a linked list?
Write a prog to accept a given string in any order and flash error if any of the
character is different. For example : If abc is the input then abc, bca, cba, cab bac are
acceptable but aac or bcd are unacceptable.
This is a C question that I had for an intern position at Microsoft: Write out a function
that prints out all the permutations of a string. For example, abc would give you abc, acb, bac,
bca, cab, cba. You can assume that all the characters will be unique. After I wrote out my
function, he asked me to figure out from the code how many times the printf statement is run,
and also questions on optimizing my algorithm.
#include <stdio.h>
main()
{
typedef union
{
int a;
char b[10];
float c;
}
Union;
printf(\"Union x : %d %s %f \n\",x.a,x.b,x.c );
printf(\"Union y :%d %s%f \n\",y.a,y.b,y.c);
}
Given inputs X, Y, Z and operations | and & (meaning bitwise OR and AND, respectively)
^Back to Top
This set of questions came from a prominent gaming company. As you can see, the answers
are not given (the interviews are typically conducted by senior developers), but there’s a set
of notes with common mistakes to avoid.
1. Explain which of the following declarations will compile and what will be
constant - a pointer or the value pointed at:
o const char *
o char const *
o char * const
Note: Ask the candidate whether the first declaration is pointing to a string or a single
character. Both explanations are correct, but if he says that it’s a single character pointer, ask
why a whole string is initialized as char* in C++. If he says this is a string declaration, ask
him to declare a pointer to a single character. Competent candidates should not have
problems pointing out why const char* can be both a character and a string declaration,
incompetent ones will come up with invalid reasons.
2. You’re given a simple code for the class BankCustomer. Write the following
functions:
o Copy constructor
o = operator overload
o == operator overload
o + operator overload (customers’ balances should be added up, as an example
of joint account between husband and wife)
Note:Anyone confusing assignment and equality operators should be dismissed from the
interview. The applicant might make a mistake of passing by value, not by reference. The
candidate might also want to return a pointer, not a new object, from the addition operator.
Slightly hint that you’d like the value to be changed outside the function, too, in the first case.
Ask him whether the statement customer3 = customer1 + customer2 would work in the
second case.
What constructors will be called when an instance of F is initialized? Produce the program
output when this happens.
Note: Incorrect replies: “No, everything is correct”, “Only the first element of the array will be
deleted”, “The entire array will be deleted, but only the first element destructor will be called”.
Note: Typical wrong answer: Yes, the program will crash in an attempt to delete a null pointer.
The candidate does not understand pointers. A very smart candidate will ask whether delete
is overloaded for the class T.
22. Explain virtual inheritance. Draw the diagram explaining the initialization of
the base class when virtual inheritance is used.
Note: Typical mistake for applicant is to draw an inheritance diagram, where a single
base class is inherited with virtual methods. Explain to the candidate that this is not
virtual inheritance. Ask them for the classic definition of virtual inheritance. Such
question might be too complex for a beginning or even intermediate developer, but
any applicant with advanced C++ experience should be somewhat familiar with the
concept, even though he’ll probably say he’d avoid using it in a real project. Moreover,
even the experienced developers, who know about virtual inheritance, cannot
coherently explain the initialization process. If you find a candidate that knows both
the concept and the initialization process well, he’s hired.
23. What’s potentially wrong with the following code?
24.
25. long value;
26. //some stuff
27. value &= 0xFFFF;
Note: Hint to the candidate about the base platform they’re developing for. If the person still
doesn’t find anything wrong with the code, they are not experienced with C++.
28. What does the following code do and why would anyone write something like
that?
29. void send (int *to, int * from, int count)
30. {
31. int n = (count + 7) / 8;
32. switch ( count % 8)
33. {
34. case 0: do { *to++ = *from++;
35. case 7: *to++ = *from++;
36. case 6: *to++ = *from++;
37. case 5: *to++ = *from++;
38. case 4: *to++ = *from++;
39. case 3: *to++ = *from++;
40. case 2: *to++ = *from++;
41. case 1: *to++ = *from++;
42. } while ( --n > 0 );
43. }
44. }
45. In the H file you see the following declaration:
46. class Foo {
47. void Bar( void ) const ;
48. };
5: You can use class libraries to provide robust new data types which
can be
made exceptionally easy to use.
For example, the Rogue Wave 'math.h++' class library implements general
multi-dimensional arrays which can be manipulated with high-level
operations
and an intuitive syntax:
inline Double
SumOfPositiveElements
(const DoubleVec& v)
{
Double theSum = 0;
for (int i = 0; i < v.length(); i++) {
if (v[i] > 0) {
theSum += v[i];
}
}
return theSum;
}
8: C++ Compiles most ANSI C code directly and can call compiled C code
directly, so you don't even have to learn anything new at all!
I recently spent some time converting a copy of SUPER to ANSI C on a
Macintosh and was able to compile it equally easily with C++ as well.
The
C++ version also had the advantage that it worked when I finished, I had
managed to break the C version so that it crashed almost immediately.
9: You don't have to put all of your declarations at the top of each
block
in C++. This means
that you can organise your code into logically related 'paragraphs'
complete
with their necessary declarations. This makes code much more
maintainable -
you can easily move sections of code around, taking the necessary
declarations along at the same time. If you use the const modifier you
can
also ensure that variables whose value should not change after it is
first
calculated do not do so.
10: Classes provide extensible types, promoting code reuse. This can
result in major savings in the amount of code written. I saw a recent
article which stated that the new Taligent operating system, which is
written
in C++, consists of 250,000 lines of code, whereas WindowsNT, written
in C,
was said to consist of 4,000,000 lines of code.
When a function with an exception specification throws an exception that is not listed in its exception
specification, the C++ run time does the following:
You can replace the default value of unexpected_handler with the function set_unexpected().
Although unexpected() cannot return, it may throw (or rethrow) an exception. Suppose the
exception specification of a function f() has been violated. If unexpected() throws an exception
allowed by the exception specification of f(), then the C++ run time will search for another handler at
the call of f(). The following example demonstrates this:
#include <iostream>
using namespace std;
struct E {
const char* message;
E(const char* arg) : message(arg) { }
};
void my_unexpected() {
cout << "Call to my_unexpected" << endl;
throw E("Exception thrown from my_unexpected");
}
void f() throw(E) {
cout << "In function f(), throw const char* object" << endl;
throw("Exception, type const char*, thrown from f()");
}
int main() {
set_unexpected(my_unexpected);
try {
f();
}
catch (E& e) {
cout << "Exception in main(): " << e.message << endl;
}
}
The main() function's try block calls function f(). Function f() throws an object of type const
char*. However the exception specification of f() allows only objects of type E to be thrown. The
function unexpected() is called. The function unexpected() calls my_unexpected(). The
function my_unexpected() throws an object of type E. Since unexpected() throws an object
allowed by the exception specification of f(), the handler in the main() function may handle the
exception.
If unexpected() did not throw (or rethrow) an object allowed by the exception specification of f(),
then the C++ run time does one of two things:
The function unexpected(), when invoked, calls the function most recently supplied as an argument
to set_unexpected(). If set_unexpected() has not yet been called, unexpected() calls
terminate().
The function terminate(), when invoked, calls the function most recently supplied as an argument
to set_terminate(). If set_terminate() has not yet been called, terminate() calls abort(),
which ends the program.
You can use set_unexpected() and set_terminate() to register functions you define to be
called by unexpected() and terminate(). The functions set_unexpected() and
set_terminate() are included in the standard header files. Each of these functions has as its return
type and its argument type a pointer to function with a void return type and no arguments. The pointer
to function you supply as the argument becomes the function called by the corresponding special
function: the argument to set_unexpected() becomes the function called by unexpected(), and
the argument to set_terminate() becomes the function called by terminate().
Both set_unexpected() and set_terminate() return a pointer to the function that was
previously called by their respective special functions (unexpected() and terminate()). By saving
the return values, you can restore the original special functions later so that unexpected() and
terminate() will once again call terminate() and abort().
If you use set_terminate() to register your own function, the function should no return to its caller
but terminate execution of the program.
When an exception is thrown and control passes from a try block to a handler, the C++ run time calls
destructors for all automatic objects constructed since the beginning of the try block. This process is
called stack unwinding. The automatic objects are destroyed in reverse order of their construction.
(Automatic objects are local objects that have been declared auto or register, or not declared
static or extern. An automatic object x is deleted whenever the program exits the block in which x
is declared.)
If during stack unwinding a destructor throws an exception and that exception is not handled, the
terminate() function is called. The following example demonstrates this:
#include <iostream>
using namespace std;
struct E {
const char* message;
E(const char* arg) : message(arg) { }
};
void my_terminate() {
cout << "Call to my_terminate" << endl;
};
struct A {
A() { cout << "In constructor of A" << endl; }
~A() {
cout << "In destructor of A" << endl;
throw E("Exception thrown in ~A()");
}
};
struct B {
B() { cout << "In constructor of B" << endl; }
~B() { cout << "In destructor of B" << endl; }
};
int main() {
set_terminate(my_terminate);
try {
cout << "In try block" << endl;
A a;
B b;
throw("Exception thrown in try block of main()");
}
catch (const char* e) {
cout << "Exception: " << e << endl;
}
catch (...) {
cout << "Some exception caught in main()" << endl;
}
In try block
In constructor of A
In constructor of B
In destructor of B
In destructor of A
Call to my_terminate
In the try block, two automatic objects are created: a and b. The try block throws an exception of type
const char*. The handler catch (const char* e) catches this exception. The C++ run time
unwinds the stack, calling the destructors for a and b in reverse order of their construction. The
destructor for a throws an exception. Since there is no handler in the program that can handle this
exception, the C++ run time calls terminate(). (The function terminate() calls the function
specified as the argument to set_terminate(). In this example, terminate() has been specified
to call my_terminate().)
You overload operator-> with a nonstatic member function that has no parameters. The following
example demonstrates how the compiler interprets overloaded class member access operators:
struct Y {
void f() { };
};
struct X {
Y* ptr;
Y* operator->() {
return ptr;
};
};
int main() {
X x;
x->f();
}
The operator-> is used (often in conjunction with the pointer-dereference operator) to implement
"smart pointers." These pointers are objects that behave like normal pointers except they perform
other tasks when you access an object through them, such as automatic object deletion (either when
the pointer is destroyed, or the pointer is used to point to another object), or reference counting
(counting the number of smart pointers that point to the same object, then automatically deleting the
object when that count reaches zero).
One example of a smart pointer is included in the C++ Standard Library called auto_ptr. You can
find it in the <memory> header. The auto_ptr class implements automatic object deletion.
You overload a function name f by declaring more than one function with the name f in the same
scope. The declarations of f must differ from each other by the types and/or the number of arguments
in the argument list. When you call an overloaded function named f, the correct function is selected by
comparing the argument list of the function call with the parameter list of each of the overloaded
candidate functions with the name f. A candidate function is a function that can be called based on the
context of the call of the overloaded function name.
Consider a function print, which displays an int. As shown in the following example, you can
overload the function print to display other types, for example, double and char*. You can have
three functions with the same name, each performing a similar operation on a different data type:
#include <iostream>
using namespace std;
void print(int i) {
cout << " Here is int " << i << endl;
}
void print(double f) {
cout << " Here is float " << f << endl;
}
void print(char* c) {
cout << " Here is char* " << c << endl;
}
int main() {
print(10);
print(10.10);
print("ten");
}
Here is int 10
Here is float 10.1
Here is char* ten
You cannot overload the following function declarations if they appear in the same scope. Note that
this list applies only to explicitly declared functions and those that have been introduced through
using declarations:
• Function declarations that differ only by return type. For example, you cannot declare the
following declarations:
• int f();
float f();
• Member function declarations that have the same name and the same parameter types, but
one of these declarations is a static member function declaration. For example, you cannot
declare the following two member function declarations of f():
• struct A {
• static int f();
• int f();
};
• Member function template declarations that have the same name, the same parameter types,
and the same template parameter lists, but one of these declarations is a static template
member function declaration.
• Function declarations that have equivalent parameter declarations. These declarations are not
allowed because they would be declaring the same function.
• Function declarations with parameters that differ only by the use of typedef names that
represent the same type. Note that a typedef is a synonym for another type, not a separate
type. For example, the following two declarations of f() are declarations of the same
function:
• typedef int I;
• void f(float, int);
• void f(float, I);
• Function declarations with parameters that differ only because one is a pointer and the other
is an array. For example, the following are declarations of the same function:
• f(char*);
• f(char[10]);
The first array dimension is insignificant when differentiating parameters; all other array
dimensions are significant. For example, the following are declarations of the same function:
g(char(*)[20]);
g(char[5][20]);
The following two declarations are not equivalent:
g(char(*)[20]);
g(char(*)[40]);
• Function declarations with parameters that differ only because one is a function type and the
other is a pointer to a function of the same type. For example, the following are declarations of
the same function:
• void f(int(float));
void f(int (*)(float));
• Function declarations with parameters that differ only because of cv-qualifiers const,
volatile, and restrict. This restriction only applies if any of these qualifiers appears at
the outermost level of a parameter type specification. For example, the following are
declarations of the same function:
• int f(int);
• int f(const int);
int f(volatile int);
Note that you can differentiate parameters with const, volatile and restrict qualifiers if
you apply them within a parameter type specification. For example, the following declarations
are not equivalent because const and volatile qualify int, rather than *, and thus are not
at the outermost level of the parameter type specification.
void g(int*);
void g(const int*);
void g(volatile int*);
The following declarations are also not equivalent:
void g(float&);
void g(const float&);
void g(volatile float&);
• Function declarations with parameters that differ only because their default arguments differ.
For example, the following are declarations of the same function:
• void f(int);
void f(int i = 10);
• Multiple functions with extern "C" language-linkage and the same name, regardless of
whether their parameter lists are different.
>>-using--namespace--::--member--------------------------------><
In this syntax diagram, the qualifier name follows the using declaration and the member follows the
qualifier name. For the declaration to work, the member must be declared inside the given
namespace. For example:
namespace A {
int i;
int k;
void f;
void g;
}
using A::k
In this example, the using declaration is followed by A, the name of namespace A, which is then
followed by the scope operator (::), and k. This format allows k to be accessed outside of namespace
A through a using declaration. After issuing a using declaration, any extension made to that specific
namespace will not be known at the point at which the using declaration occurs.
Overloaded versions of a given function must be included in the namespace prior to that given
function's declaration. A using declaration may appear at namespace, block and class scope.
A using declaration in a definition of a class A allows you to introduce a name of a data member or
member function from a base class of A into the scope of A.
You would need a using declaration in a class definition if you want to create a set of overload a
member functions from base and derived classes, or you want to change the access of a class
member.
>>-using--+-+----------+--+----+--nested_name_specifier--unqualified_id--;-
+-><
| '-typename-' '-::-'
|
'-::--
unqualified_id--;------------------------------------------'
struct Z {
int g();
};
struct A {
void f();
enum E { e };
union { int u; };
};
struct B : A {
using A::f;
using A::e;
using A::u;
// using Z::g;
};
The compiler would not allow the using declaration using Z::g because Z is not a base class of A.
A using declaration cannot name a template. For example, the compiler will not allow the following:
struct A {
template<class T> void f(T);
};
struct B : A {
using A::f<int>;
};
Every instance of the name mentioned in a using declaration must be accessible. The following
example demonstrates this:
struct A {
private:
void f(int);
public:
int f();
protected:
void g();
};
struct B : A {
// using A::f;
using A::g;
};
The compiler would not allow the using declaration using A::f because void A::f(int) is not
accessible from B even though int A::f() is accessible.
You overload a unary operator with either a nonstatic member function that has no parameters, or a
nonmember function that has one parameter. Suppose a unary operator @ is called with the statement
@t, where t is an object of type T. A nonstatic member function that overloads this operator would
have the following form:
return_type operator@()
A nonmember function that overloads the same operator would have the following form:
return_type operator@(T)
#include <iostream>
using namespace std;
struct X { };
void operator!(X) {
cout << "void operator!(X)" << endl;
}
struct Y {
void operator!() {
cout << "void Y::operator!()" << endl;
}
};
struct Z { };
int main() {
X ox; Y oy; Z oz;
!ox;
!oy;
// !oz;
}
void operator!(X)
void Y::operator!()
The operator function call !ox is interpreted as operator!(X). The call !oy is interpreted as
Y::operator!().
(The compiler would not allow !oz because the ! operator has not been defined for class Z.)
Most of what I have to say about goto really only applies to C. If you're using C++, there's no
sound reason to use goto in place of exceptions. In C, however, you don't have the power of an
exception handling mechanism, so if you want to separate out error handling from the rest of your
program logic, and you want to avoid rewriting clean up code multiple times throughout your code,
then goto can be a good choice.
What do I mean? You might have some code that looks like this:
int big_function()
{
/* do some work */
if([error])
{
/* clean up*/
return [error];
}
/* do some more work */
if([error])
{
/* clean up*/
return [error];
}
/* do some more work */
if([error])
{
/* clean up*/
return [error];
}
/* do some more work */
if([error])
{
/* clean up*/
return [error];
}
/* clean up*/
return [success];
}
This is fine until you realize that you need to change your cleanup code. Then you have to go
through and make 4 changes. Now, you might decide that you can just encapsulate all of the
cleanup into a single function; that's not a bad idea. But it does mean that you'll need to be careful
with pointers -- if you plan to free a pointer in your cleanup function, there's no way to set it to then
point to NULL unless you pass in a pointer to a pointer. In a lot of cases, you won't be using that
pointer again anyway, so that may not be a major concern. On the other hand, if you add in a new
pointer, file handle, or other thing that needs cleanup, then you'll need to change your cleanup
function again; and then you'll need to change the arguments to that function.
In some cases, this might be acceptable -- but if you're making a lot of changes to your code,
adding in new variables, etc. -- it may not be worth the time or the extra lines of code to make that
function call.
Instead, since you know that you're going to be executing only one piece of code and then returning
from the function, you might as well use a goto to jump to the very end of the function, where you
have your cleanup code.
Goto is a pretty simple keyword: you just need to include a "label" placed above the target location
(followed by a colon), and then direct the program to go to the label. Note that this only works
within the same function; you can't just enter one function from another.
goto label;
/* Code
...
*/
label:
Now, using this, if we want to handle all our errors in one place, we'll need to add in a variable to
track the return value of our function so we can return it:
int big_function()
{
int ret_val = [success];
/* do some work */
if([error])
{
ret_val = [error];
goto end;
}
/* do some more work */
if([error])
{
ret_val = [error];
goto end;
}
/* do some more work */
if([error])
{
ret_val = [error];
goto end;
}
/* do some more work */
if([error])
{
ret_val = [error];
goto end;
}
end:
/* clean up*/
return ret_val;
}
The benefit here is that your code following end has access to everything it will need to perform
cleanup, and you've managed to reduce the number of change points considerably. Another benefit
is that you've gone from having multiple exit points for your function to just one; there's no chance
you'll accidentally return from the function without cleaning up.
Moreover, since goto is only being used to jump to a single point, it's not as though you're creating
a mass of spaghetti code jumping back and forth in an attempt to simulate function calls. Rather,
goto actually helps write more structured code.
There is one thing to be aware of: while your cleanup code should be able to free all of the memory
you use, there may be times when you actually want to free that memory yourself and possibly
reallocate it later. In these cases, if you do call free on a ptr and then have an if([error]) between
that call to free and the subsequent call to malloc, you should definitely set the pointer to point to
NULL! This will prevent your jumping to the cleanup code and then calling free on that pointer a
second time, which can result in a security hole (the "double free" problem).
Goto should always be used sparingly, and as a last resort -- but there is a time and a place for it.
The question should be not "do you have to use it" but "is it the best choice" to use it.
The process of selecting the most appropriate overloaded function or operator is called overload
resolution.
Suppose that f is an overloaded function name. When you call the overloaded function f(), the
compiler creates a set of candidate functions. This set of functions includes all of the functions named
f that can be accessed from the point where you called f(). The compiler may include as a candidate
function an alternative representation of one of those accessible functions named f to facilitate
overload resolution.
After creating a set of candidate functions, the compiler creates a set of viable functions. This set of
functions is a subset of the candidate functions. The number of parameters of each viable function
agrees with the number of arguments you used to call f().
The compiler chooses the best viable function, the function declaration that the C++ run-time
environment will use when you call f(), from the set of viable functions. The compiler does this by
implicit conversion sequences. An implicit conversion sequence is the sequence of conversions
required to convert an argument in a function call to the type of the corresponding parameter in a
function declaration. The implicit conversion sequences are ranked; some implicit conversion
sequences are better than others. The best viable function is the one whose parameters all have either
better or equal-ranked implicit conversion sequences than all of the other viable functions. The
compiler will not allow a program in which the compiler was able to find more than one best viable
function. Implicit conversion sequences are described in more detail in Implicit conversion sequences
(C++ only).
When a variable length array is a function parameter, the leftmost array dimension does not
distinguish functions among candidate functions. In the following, the second definition of f is not
allowed because void f(int []) has already been defined.
However, array dimensions other than the leftmost in a variable length array do differentiate candidate
functions when the variable length array is a function parameter. For example, the overload set for
function f might comprise the following:
You can override an exact match by using an explicit cast. In the following example, the second call to
f() matches with f(void*):
void f(int) { };
void f(void*) { };
int main() {
f(0xaabb); // matches f(int);
f((void*) 0xaabb); // matches f(void*)
}
The keyword this identifies a special type of pointer. Suppose that you create an object named x of
class A, and class A has a nonstatic member function f(). If you call the function x.f(), the keyword
this in the body of f() stores the address of x. You cannot declare the this pointer or make
assignments to it.
The type of the this pointer for a member function of a class type X, is X* const. If the member
function is declared with the const qualifier, the type of the this pointer for that member function for
class X, is const X* const.
A const this pointer can by used only with const member functions. Data members of the class
will be constant within that function. The function is still able to change the value, but requires a
const_cast to do so:
If the member function is declared with the volatile qualifier, the type of the this pointer for that
member function for class X is volatile X* const. For example, the compiler will not allow the
following:
struct A {
int a;
int f() const { return a++; }
};
The compiler will not allow the statement a++ in the body of function f(). In the function f(), the
this pointer is of type A* const. The function f() is trying to modify part of the object to which
this points.
The this pointer is passed as a hidden argument to all nonstatic member function calls and is
available as a local variable within the body of all nonstatic functions.
For example, you can refer to the particular class object that a member function is called for by using
the this pointer in the body of the member function. The following code example produces the output
a = 5:
#include <iostream>
using namespace std;
struct X {
private:
int a;
public:
void Set_a(int a) {
int main() {
X xobj;
int a = 5;
xobj.Set_a(a);
xobj.Print_a();
}
In the member function Set_a(), the statement this->a = a uses the this pointer to retrieve
xobj.a hidden by the automatic variable a.
Unless a class member name is hidden, using the class member name is equivalent to using the class
member name with the this pointer and the class member access operator (->).
The example in the first column of the following table shows code that uses class members without the
this pointer. The code in the second column uses the variable THIS to simulate the first column's
hidden use of the this pointer:
Code without using this pointer Equivalent code, the THIS variable simulating the hidden use of the this pointer
struct X { struct X {
private: private:
int len; int len;
char *ptr; char *ptr;
public: public:
int GetLen() { int GetLen (X* const THIS) {
return len; return THIS->len;
} }
char * GetPtr() { char * GetPtr (X* const THIS) {
return ptr; return THIS->ptr;
} }
X& Set(char *); X& Set(X* const, char *);
X& Cat(char *); X& Cat(X* const, char *);
X& Copy(X&); X& Copy(X* const, X&);
void Print(); void Print(X* const);
}; };
xobj1.Print(); xobj1.Print(&xobj1);
X xobj2; X xobj2;
xobj2.Copy(xobj1) xobj2.Copy(&xobj2 , xobj1)
.Cat("ijkl"); .Cat(&xobj2 , "ijkl");
xobj2.Print(); xobj2.Print(&xobj2);
} }
abcdefgh
abcdefghijkl
If you do not declare a copy constructor for a class A, the compiler will implicitly declare one for you,
which will be an inline public member.
The following example demonstrates implicitly defined and user-defined copy constructors:
#include <iostream>
using namespace std;
struct A {
int i;
A() : i(10) { }
};
struct B {
int j;
B() : j(20) {
cout << "Constructor B(), j = " << j << endl;
}
struct C {
C() { }
C(C&) { }
};
int main() {
A a;
A a1(a);
B b;
const B b_const;
B b1(b);
B b2(b_const);
const C c_const;
// C c1(c_const);
}
Constructor B(), j = 20
Constructor B(), j = 20
Copy constructor B(B&), j = 20
Copy constructor B(const B&, int), j = 30
The statement A a1(a) creates a new object from a with an implicitly defined copy constructor. The
statement B b1(b) creates a new object from b with the user-defined copy constructor B::B(B&).
The statement B b2(b_const) creates a new object with the copy constructor B::B(const B&,
int). The compiler would not allow the statement C c1(c_const) because a copy constructor that
takes as its first parameter an object of type const C& has not been defined.
The implicitly declared copy constructor of a class A will have the form A::A(const A&) if the
following are true:
• The direct and virtual bases of A have copy constructors whose first parameters have been
qualified with const or const volatile
• The nonstatic class type or array of class type data members of A have copy constructors
whose first parameters have been qualified with const or const volatile
If the above are not true for a class A, the compiler will implicitly declare a copy constructor with the
form A::A(A&).
The compiler cannot allow a program in which the compiler must implicitly define a copy constructor for
a class A and one or more of the following are true:
• Class A has a nonstatic data member of a type which has an inaccessible or ambiguous copy
constructor.
• Class A is derived from a class which has an inaccessible or ambiguous copy constructor.
The compiler will implicitly define an implicitly declared constructor of a class A if you initialize an
object of type A or an object derived from class A.
An implicitly defined copy constructor will copy the bases and members of an object in the same order
that a constructor would initialize the bases and members of the object
Because classes have complicated internal structures, including data and functions, object initialization
and cleanup for classes is much more complicated than it is for simple data structures. Constructors
and destructors are special member functions of classes that are used to construct and destroy class
objects. Construction may involve memory allocation and initialization for objects. Destruction may
involve cleanup and deallocation of memory for objects.
Like other member functions, constructors and destructors are declared within a class declaration.
They can be defined inline or external to the class declaration. Constructors can have default
arguments. Unlike other member functions, constructors can have member initialization lists. The
following restrictions apply to constructors and destructors:
• Constructors and destructors do not have return types nor can they return values.
• References and pointers cannot be used on constructors and destructors because their
addresses cannot be taken.
• Constructors cannot be declared with the keyword virtual.
• Constructors and destructors cannot be declared static, const, or volatile.
• Unions cannot contain class objects that have constructors or destructors.
Constructors and destructors obey the same access rules as member functions. For example, if you
declare a constructor with protected access, only derived classes and friends can use it to create class
objects.
The compiler automatically calls constructors when defining class objects and calls destructors when
class objects go out of scope. A constructor does not allocate memory for the class object its this
pointer refers to, but may allocate storage for more objects than its class object refers to. If memory
allocation is required for objects, constructors can explicitly call the new operator. During cleanup, a
destructor may release objects allocated by the corresponding constructor. To release objects, use the
delete operator.
Derived classes do not inherit or overload constructors or destructors from their base classes, but they
do call the constructor and destructor of base classes. Destructors can be declared with the keyword
virtual.
Constructors are also called when local or temporary class objects are created, and destructors are
called when local or temporary objects go out of scope.
You can call member functions from constructors or destructors. You can call a virtual function, either
directly or indirectly, from a constructor or destructor of a class A. In this case, the function called is the
one defined in A or a base class of A, but not a function overridden in any class derived from A. This
avoids the possibility of accessing an unconstructed object from a constructor or destructor. The
following example demonstrates this:
#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "void A::f()" << endl; }
virtual void g() { cout << "void A::g()" << endl; }
virtual void h() { cout << "void A::h()" << endl; }
};
struct B : A {
virtual void f() { cout << "void B::f()" << endl; }
B() {
f();
g();
h();
}
};
struct C : B {
virtual void f() { cout << "void C::f()" << endl; }
virtual void g() { cout << "void C::g()" << endl; }
virtual void h() { cout << "void C::h()" << endl; }
};
int main() {
C obj;
}
The constructor of B does not call any of the functions overridden in C because C has been derived
from B, although the example creates an object of type C named obj.
You can use the typeid or the dynamic_cast operator in constructors or destructors, as well as
member initializers of constructors.
>>-+----+--new--+---------------------+--+-(--type--)-+--------->
'-::-' '-(--argument_list--)-' '-new_type---'
>--+-------------------------+---------------------------------><
'-(--+---------------+--)-'
'-initial_value-'
If you prefix new with the scope resolution operator (::), the global operator new() is used. If you
specify an argument_list, the overloaded new operator that corresponds to that argument_list
is used. The type is an existing built-in or user-defined type. A new_type is a type that has not
already been defined and can include type specifiers and declarators.
An allocation expression containing the new operator is used to find storage in free store for the object
being created. The new expression returns a pointer to the object created and can be used to initialize
the object. If the object is an array, a pointer to the initial element is returned.
You cannot use the new operator to allocate function types, void, or incomplete class types because
these are not object types. However, you can allocate pointers to functions with the new operator. You
cannot create a reference with the new operator.
When the object being created is an array, only the first dimension can be a general expression. All
subsequent dimensions must be constant integral expressions. The first dimension can be a general
expression even when an existing type is used. You can create an array with zero bounds with the
new operator. For example:
An object created with operator new() or operator new[]() exists until the operator
delete() or operator delete[]() is called to deallocate the object's memory. A delete
operator or a destructor will not be implicitly called for an object created with a new that has not been
explicitly deallocated before the end of the program.
If parentheses are used within a new type, parentheses should also surround the new type to prevent
syntax errors.
In the following example, storage is allocated for an array of pointers to functions:
void f();
void g();
int main(void)
{
void (**p)(), (**q)();
// declare p and q as pointers to pointers to void functions
p = new (void (*[3])());
// p now points to an array of pointers to functions
q = new void(*[3])(); // error
// error - bound as 'q = (new void) (*[3])();'
p[0] = f; // p[0] to point to function f
q[2] = g; // q[2] to point to function g
p[0](); // call f()
q[2](); // call g()
return (0);
}
However, the second use of new causes an erroneous binding of q = (new void) (*[3])().
The type of the object being created cannot contain class declarations, enumeration declarations, or
const or volatile types. It can contain pointers to const or volatile objects.
Placement syntax
Arguments specifying an allocated storage location can be supplied to new by using the
argument_list, also called the placement syntax. If placement arguments are used, a declaration of
operator new() or operator new[]() with these arguments must exist. For example:
#include <new>
using namespace std;
class X
{
public:
void* operator new(size_t,int, int){ /* ... */ }
};
// ...
int main ()
{
X* ptr = new(1,2) X;
}
The placement syntax is commonly used to invoke the global placement new function. The global
placement new function initializes an object or objects at the location specified by the placement
argument in the placement new expression. This location must address storage that has previously
been allocated by some other means, because the global placement new function does not itself
allocate memory. In the following example, no new memory is allocated by the calls new(whole)
X(8);, new(seg2) X(9);, or new(seg3) X(10); Instead, the constructors X(8), X(9), and
X(10) are called to reinitialize the memory allocated to the buffer whole.
Because placement new does not allocate memory, you should not use delete to deallocate objects
created with the placement syntax. You can only delete the entire memory pool (delete whole). In
the example, you can keep the memory buffer but destroy the object stored in it by explicitly calling a
destructor.
#include <new>
class X
{
public:
X(int n): id(n){ }
~X(){ }
private:
int id;
//...
};
int main()
{
char* whole = new char[ 3 * sizeof(X) ]; // a 3-part buffer
X * p1 = new(whole) X(8); // fill the front
char* seg2 = &whole[ sizeof(X) ]; // mark second segment
X * p2 = new(seg2) X(9); // fill second segment
char* seg3 = &whole[ 2 * sizeof(X) ]; // mark third segment
X * p3 = new(seg3) X(10); // fill third segment
The placement new syntax can also be used for passing parameters to an allocation routine rather
than to a constructor.
Free store is a pool of memory available for you to allocate (and deallocate) storage for objects during
the execution of your program. The new and delete operators are used to allocate and deallocate
free store, respectively.
You can define your own versions of new and delete for a class by overloading them. You can
declare the new and delete operators with additional parameters. When new and delete operate on
class objects, the class member operator functions new and delete are called, if they have been
declared.
If you create a class object with the new operator, one of the operator functions operator new() or
operator new[]() (if they have been declared) is called to create the object. An operator new()
or operator new[]() for a class is always a static class member, even if it is not declared with the
keyword static. It has a return type void* and its first parameter must be the size of the object type
and have type std::size_t. It cannot be virtual.
• X::operator new(size_t sz): This overloads the default new operator by allocating
memory with the C function malloc(), and throwing a string (instead of std::bad_alloc)
if malloc() fails.
• X::operator new(size_t sz, int location): This function takes an additional
integer parameter, location. This function implements a very simplistic "memory manager"
that manages the storage of up to three X objects.
Static array X::buffer holds three Node objects. Each Node object contains a pointer to an
X object named data and a Boolean variable named filled. Each X object stores an integer
called number.
When you use this new operator, you pass the argument location which indicates the array
location of buffer where you want to "create" your new X object. If the array location is not
"filled" (the data member of filled is equal to false at that array location), the new
operator returns a pointer pointing to the X object located at buffer[location].
#include <new>
#include <iostream>
class X;
struct Node {
X* data;
bool filled;
Node() : filled(false) { }
};
class X {
static Node buffer[];
public:
int number;
};
Node X::buffer[size];
int main() {
try {
X* ptr1 = new X;
X* ptr2 = new(0) X;
X* ptr3 = new(1) X;
X* ptr4 = new(2) X;
ptr2->number = 10000;
ptr3->number = 10001;
ptr4->number = 10002;
X::printbuffer();
X* ptr5 = new(0) X;
}
catch (const char* message) {
cout << message << endl;
}
}
X::operator new(size_t)
X::operator new(size_t, 0)
X::operator new(size_t, 1)
X::operator new(size_t, 2)
10000
10001
10002
X::operator new(size_t, 0)
Error: buffer location occupied
The statement X* ptr1 = new X calls X::operator new(sizeof(X)). The statement X* ptr2
= new(0) X calls X::operator new(sizeof(X),0).
The delete operator destroys an object created by the new operator. The operand of delete must
be a pointer returned by new. If delete is called for an object with a destructor, the destructor is
invoked before the object is deallocated.
If you destroy a class object with the delete operator, the operator function operator delete() or
operator delete[]() (if they have been declared) is called to destroy the object. An operator
delete() or operator delete[]() for a class is always a static member, even if it is not declared
with the keyword static. Its first parameter must have type void*. Because operator delete()
and operator delete[]() have a return type void, they cannot return a value.
The following example shows the declaration and use of the operator functions operator new() and
operator delete():
#include <cstdlib>
#include <iostream>
using namespace std;
class X {
public:
void* operator new(size_t sz) throw (const char*) {
void* p = malloc(sz);
if (p == 0) throw "malloc() failed";
return p;
}
// single argument
void operator delete(void* p) {
cout << "X::operator delete(void*)" << endl;
free(p);
}
};
class Y {
int filler[100];
public:
// two arguments
void operator delete(void* p, size_t sz) throw (const char*) {
cout << "Freeing " << sz << " byte(s)" << endl;
free(p);
};
};
int main() {
X* ptr = new X;
Y* yptr = new Y;
X::operator delete(void*)
Freeing 400 byte(s)
The statement delete ptr calls X::operator delete(void*). The statement delete yptr calls
Y::operator delete(void*, size_t).
The result of trying to access a deleted object is undefined because the value of the object can change
after deletion.
If new and delete are called for a class object that does not declare the operator functions new and
delete, or they are called for a nonclass object, the global operators new and delete are used. The
global operators new and delete are provided in the C++ library.
The C++ operators for allocating and deallocating arrays of class objects are operator new[ ]()
and operator delete[ ]().
You cannot declare the delete operator as virtual. However you can add polymorphic behavior to
your delete operators by declaring the destructor of a base class as virtual. The following example
demonstrates this:
#include <iostream>
using namespace std;
struct A {
virtual ~A() { cout << "~A()" << endl; };
void operator delete(void* p) {
cout << "A::operator delete" << endl;
free(p);
}
};
struct B : A {
void operator delete(void* p) {
cout << "B::operator delete" << endl;
free(p);
}
};
int main() {
A* ap = new B;
delete ap;
}
~A()
B::operator delete
The statement delete ap uses the delete operator from class B instead of class A because the
destructor of A has been declared as virtual.
Although you can get polymorphic behavior from the delete operator, the delete operator that is
statically visible must still be accessible even though another delete operator might be called. For
example, in the above example, the function A::operator delete(void*) must be accessible
even though the example calls B::operator delete(void*) instead.
Virtual destructors do not have any affect on deallocation operators for arrays (operator delete[]
()). The following example demonstrates this:
#include <iostream>
using namespace std;
struct A {
virtual ~A() { cout << "~A()" << endl; }
void operator delete[](void* p, size_t) {
cout << "A::operator delete[]" << endl;
::delete [] p;
}
};
struct B : A {
void operator delete[](void* p, size_t) {
cout << "B::operator delete[]" << endl;
::delete [] p;
}
};
int main() {
A* bp = new B[3];
delete[] bp;
};
When you overload the delete operator, you must declare it as class member, returning type void,
with the first parameter having type void*, as described above. You can add a second parameter of
type size_t to the declaration. You can only have one operator delete() or operator
delete[]() for a single class.
The delete operator destroys the object created with new by deallocating the memory associated
with the object.
>>-+----+--delete--object_pointer------------------------------><
'-::-'
The operand of delete must be a pointer returned by new, and cannot be a pointer to constant.
Deleting a null pointer has no effect.
The delete[] operator frees storage allocated for array objects created with new[]. The delete
operator frees storage allocated for individual objects created with new.
>>-+----+--delete--[--]--array---------------------------------><
'-::-'
The result of deleting an array object with delete is undefined, as is deleting an individual object with
delete[]. The array dimensions do not need to be specified with delete[].
The default global operator delete() only frees storage allocated by the default global operator
new(). The default global operator delete[]() only frees storage allocated for arrays by the
default global operator new[]().
When a program begins running, the system calls the function main, which marks the entry point of
the program. By default, main has the storage class extern. Every program must have one function
named main, and the following constraints apply:
The function main can be defined with or without parameters, using any of the following forms:
Although any name can be given to these parameters, they are usually referred to as argc and argv.
The first parameter, argc (argument count) is an integer that indicates how many arguments were
entered on the command line when the program was started. The second parameter, argv (argument
vector), is an array of pointers to arrays of character objects. The array objects are null-terminated
strings, representing the arguments that were entered on the command line when the program was
started.
The first element of the array, argv[0], is a pointer to the character array that contains the program
name or invocation name of the program that is being run from the command line. argv[1] indicates
the first argument passed to the program, argv[2] the second argument, and so on.
The following example program backward prints the arguments entered on a command line such that
the last argument is printed first:
#include <stdio.h>
int main(int argc, char *argv[])
{
while (--argc > 0)
printf("%s ", argv[argc]);
}
string2 string1
The arguments argc and argv would contain the following values:
Object Value
argc 3
argv[3] NULL
An inline function is one for which the compiler copies the code from the function definition directly into
the code of the calling function rather than creating a separate set of instructions in memory. Instead of
transferring control to and from the function code segment, a modified copy of the function body may
be substituted directly for the function call. In this way, the performance overhead of a function call is
avoided.
Any function, with the exception of main, can be declared or defined as inline with the
inline function specifier. Static local variables are not allowed to be defined within the body of an
inline function.
C++ functions implemented inside of a class declaration are automatically defined inline.
Regular C++ functions and member functions declared outside of a class declaration, with the
exception of main, can be declared or defined as inline with the inline function specifier. Static
locals and string literals defined within the body of an inline function are treated as the same object
across translation units; see Linkage of inline functions for details.
The use of the inline specifier does not change the meaning of the function. However, the inline
expansion of a function may not preserve the order of evaluation of the actual arguments.
The most efficient way to code an inline function is to place the inline function definition in a header
file, and then include the header in any file containing a call to the function which you would like to
inline.
Note:
The inline qualifier is represented by the following keywords:
• The inline keyword is only recognized under compilation with c99 or with the
-qlanglvl=stdc99 or -qlanglvl=extc99 options (or equivalent pragmas) or -qkeyword=inline.
Note that the latter option is enabled by default for xlc in the configuration file that is shipped
with the compiler. The __inline__ keyword is recognized at all language levels; however,
see Linkage of inline functions below for the semantics of this keyword.
• The inline and __inline__ keywords are recognized at all language levels.
Related information
In C, inline functions are treated by default as having static linkage; that is, they are only visible within
a single translation unit. Therefore, in the following example, even though function foo is defined in
exactly the same way, foo in file a.c and foo in file b.c are treated as separate functions: two
function bodies are generated, and assigned two different addresses in memory:
// a.c
#include <stdio.h>
void g() {
printf("foo called from g: return value = %d, address = %p\n", foo(),
&foo);
}
// b.c
#include <stdio.h>
inline int foo(){
return 3;
}
void g();
int main() {
printf("foo called from main: return value = %d, address = %p\n", foo(),
&foo);
g();
}
Since inline functions are treated as having internal linkage, an inline function definition can co-exist
with a regular, external definition of a function with the same name in another translation unit.
However, when you call the function from the file containing the inline definition, the compiler may
choose either the inline version defined in the same file or the external version defined in another file
for the call; your program should not rely on the inline version being called. In the following example,
the call to foo from function g could return either 6 or 3:
// a.c
#include <stdio.h>
void g() {
printf("foo called from g: return value = %d\n", foo());
}
// b.c
#include <stdio.h>
int foo(){
return 3;
}
void g();
int main() {
printf("foo called from main: return value = %d\n", foo());
g();
}
Similarly, if you define a function as extern inline, or redeclare an inline function as extern,
the function simply becomes a regular, external function and is not inlined.
If you specify the __inline__ keyword, with the trailing underscores, the compiler uses the
GNU C semantics for inline functions. In contrast to the C99 semantics, a function defined as
__inline__ provides an external definition only; a function defined as static __inline__
provides an inline definition with internal linkage (as in C99); and a function defined as extern
__inline__, when compiled with optimization enabled, allows the co-existence of an inline and
external definition of the same function. For more information on the GNU C implementation of inline
functions, see the GCC documentation, available at http://gcc.gnu.org/onlinedocs/.
End of C only
C++ only
You must define an inline function in exactly the same way in each translation unit in which the
function is used or called. Furthermore, if a function is defined as inline, but never used or called
within the same translation unit, it is discarded by the compiler (unless you compile with the
-qkeepinlines option).
Nevertheless, in C++, inline functions are treated by default as having external linkage, meaning that
the program behaves as if there is only one copy of the function. The function will have the same
address in all translation units and each translation unit will share any static locals and string literals.
Therefore, compiling the previous example gives the following output:
Therefore, the following example, in which inline function foo is defined differently in two different files,
may not produce the expected results:
// a.C
#include <stdio.h>
void g() {
printf("foo called from g: return value = %d, address = %p\n", foo(),
&foo);
}
// b.C
#include <stdio.h>
void g();
int main() {
printf("foo called from main: return value = %d, address = %p\n", foo(),
&foo);
g();
}
When compiled with the command xlC a.C b.C, the output is:
foo called from main: return value = 6, address = 0x10001640
foo called from g: return value = 6, address = 0x10001640
The call to foo from main does not use the inline definition provided in b.C, but rather calls foo as a
regular external function defined in a.C. It is your responsibility to ensure that inline function definitions
with the same name match exactly across translation units, to avoid unexpected results.
Because inline functions are treated as having external linkage, any static local variables or string
literals that are defined within the body of an inline function are treated as the same object across
translation units. The following example demonstrates this:
// a.C
#include <stdio.h>
void g() {
printf("foo called from g: return value = %d\n", foo());
}
// b.C
#include <stdio.h>
void g();
int main() {
printf("foo called from main: return value = %d\n", foo());
g();
}
The output of this program shows that x in both definitions of foo is indeed the same object:
address of x = 0x10011d5c
foo called from main: return value = 24
address of x = 0x10011d5c
foo called from g: return value = 25
If you want to ensure that each instance of function defined as inline is treated as a separate function,
you can use the static specifier in the function definition in each translation unit, or compile with the
-qstaticinline option. Note, however, that static inline functions are removed from name lookup during
template instantiation, and are not found.
Overloading unary operators (C++ only)
You overload a unary operator with either a nonstatic member function that has no parameters, or a
nonmember function that has one parameter. Suppose a unary operator @ is called with the statement
@t, where t is an object of type T. A nonstatic member function that overloads this operator would
have the following form:
return_type operator@()
A nonmember function that overloads the same operator would have the following form:
return_type operator@(T)
#include <iostream>
using namespace std;
struct X { };
void operator!(X) {
cout << "void operator!(X)" << endl;
}
struct Y {
void operator!() {
cout << "void Y::operator!()" << endl;
}
};
struct Z { };
int main() {
X ox; Y oy; Z oz;
!ox;
!oy;
// !oz;
}
void operator!(X)
void Y::operator!()
The operator function call !ox is interpreted as operator!(X). The call !oy is interpreted as
Y::operator!().
(The compiler would not allow !oz because the ! operator has not been defined for class Z.)
Overloading assignments (C++ only)
You overload the assignment operator, operator=, with a nonstatic member function that has only
one parameter. You cannot declare an overloaded assignment operator that is a nonmember function.
The following example shows how you can overload the assignment operator for a particular class:
struct X {
int data;
X& operator=(X& a) { return a; }
X& operator=(int a) {
data = a;
return *this;
}
};
int main() {
X x1, x2;
x1 = x2; // call x1.operator=(x2)
x1 = 5; // call x1.operator=(5)
}
The assignment x1 = x2 calls the copy assignment operator X& X::operator=(X&). The
assignment x1 = 5 calls the copy assignment operator X& X::operator=(int). The compiler
implicitly declares a copy assignment operator for a class if you do not define one yourself.
Consequently, the copy assignment operator (operator=) of a derived class hides the copy
assignment operator of its base class.
However, you can declare any copy assignment operator as virtual. The following example
demonstrates this:
#include <iostream>
using namespace std;
struct A {
A& operator=(char) {
cout << "A& A::operator=(char)" << endl;
return *this;
}
virtual A& operator=(const A&) {
cout << "A& A::operator=(const A&)" << endl;
return *this;
}
};
struct B : A {
B& operator=(char) {
cout << "B& B::operator=(char)" << endl;
return *this;
}
virtual B& operator=(const A&) {
cout << "B& B::operator=(const A&)" << endl;
return *this;
}
};
struct C : B { };
int main() {
B b1;
B b2;
A* ap1 = &b1;
A* ap2 = &b1;
*ap1 = 'z';
*ap2 = b2;
C c1;
// c1 = 'z';
}
A& A::operator=(char)
B& B::operator=(const A&)
The assignment *ap1 = 'z' calls A& A::operator=(char). Because this operator has not been
declared virtual, the compiler chooses the function based on the type of the pointer ap1. The
assignment *ap2 = b2 calls B& B::operator=(const &A). Because this operator has been
declared virtual, the compiler chooses the function based on the type of the object that the pointer
ap1 points to. The compiler would not allow the assignment c1 = 'z' because the implicitly declared
copy assignment operator declared in class C hides B& B::operator=(char).
You overload a binary unary operator with either a nonstatic member function that has one parameter,
or a nonmember function that has two parameters. Suppose a binary operator @ is called with the
statement t @ u, where t is an object of type T, and u is an object of type U. A nonstatic member
function that overloads this operator would have the following form:
return_type operator@(T)
A nonmember function that overloads the same operator would have the following form:
return_type operator@(T, U)
struct X {
int main() {
X x;
int y = 10;
float z = 10;
x * y;
x * z;
}
You can redefine or overload the function of most built-in operators in C++. These operators can be
overloaded globally or on a class-by-class basis. Overloaded operators are implemented as functions
and can be member functions or global functions.
An overloaded operator is called an operator function. You declare an operator function with the
keyword operator preceding the operator. Overloaded operators are distinct from overloaded
functions, but like overloaded functions, they are distinguished by the number and types of operands
used with the operator.
Consider the standard + (plus) operator. When this operator is used with operands of different
standard types, the operators have slightly different meanings. For example, the addition of two
integers is not implemented in the same way as the addition of two floating-point numbers. C++ allows
you to define your own meanings for the standard C++ operators when they are applied to class types.
In the following example, a class called complx is defined to model complex numbers, and the +
(plus) operator is redefined in this class to add two complex numbers.
#include <iostream>
using namespace std;
class complx
{
double real,
imag;
public:
complx( double real = 0., double imag = 0.); // constructor
complx operator+(const complx&) const; // operator+()
};
// define constructor
complx::complx( double r, double i )
{
real = r; imag = i;
}
int main()
{
complx x(4,4);
complx y(6,6);
complx z = x + y; // calls complx::operator+()
}
+ - * / % ^ & | ~
! = < > += -= *= /= %=
You can overload both the unary and binary forms of the following operators:
+ - * &
. .* :: ?:
An operator function can be either a nonstatic member function, or a nonmember function with at least
one parameter that has class, reference to class, enumeration, or reference to enumeration type.
You cannot change the precedence, grouping, or the number of operands of an operator.
An overloaded operator (except for the function call operator) cannot have default arguments or an
ellipsis in the argument list.
You must declare the overloaded =, [], (), and -> operators as nonstatic member functions to
ensure that they receive lvalues as their first operands.
The operators new, delete, new[], and delete[] do not follow the general rules described in this
section.
The function call operator, when overloaded, does not modify how functions are called. Rather, it
modifies how the operator is to be interpreted when applied to objects of a given type.
You overload the function call operator, operator(), with a nonstatic member function that has any
number of parameters. If you overload a function call operator for a class its declaration will have the
following form:
return_type operator()(parameter_list)
Unlike all other overloaded operators, you can provide default arguments and ellipses in the argument
list for the function call operator.
The following example demonstrates how the compiler interprets function call operators:
struct A {
void operator()(int a, char b, ...) { }
void operator()(char c, int d = 20) { }
};
int main() {
A a;
a(5, 'z', 'a', 0);
a('z');
// a();
}
The function call a(5, 'z', 'a', 0) is interpreted as a.operator()(5, 'z', 'a', 0). This
calls void A::operator()(int a, char b, ...). The function call a('z') is interpreted as
a.operator()('z'). This calls void A::operator()(char c, int d = 20). The compiler
would not allow the function call a() because its argument list does not match any function call
parameter list defined in class A.
class Point {
private:
int x, y;
public:
Point() : x(0), y(0) { }
Point& operator()(int dx, int dy) {
x += dx;
y += dy;
return *this;
}
};
int main() {
Point pt;
The ~ (bitwise negation) operator yields the bitwise complement of the operand. In the binary
representation of the result, every bit has the opposite value of the same bit in the binary
representation of the operand. The operand must have an integral type. The result has the same type
as the operand but is not an lvalue.
Suppose x represents the decimal value 5. The 16-bit binary representation of x is:
0000000000000101
The expression ~x yields the following result (represented here as a 16-bit binary number):
1111111111111010
1111111111111111
IBM extension
The bitwise negation operator has been extended to handle complex types. With a complex type, the
operator computes the complex conjugate of the operand by reversing the sign of the imaginary part.
Destructors are usually used to deallocate memory and do other cleanup for a class object and its
class members when the object is destroyed. A destructor is called for a class object when that object
passes out of scope or is explicitly deleted.
A destructor is a member function with the same name as its class prefixed by a ~ (tilde). For example:
class X {
public:
// Constructor for class X
X();
// Destructor for class X
~X();
};
A destructor takes no arguments and has no return type. Its address cannot be taken. Destructors
cannot be declared const, volatile, const volatile or static. A destructor can be declared
virtual or pure virtual.
If no user-defined destructor exists for a class and one is needed, the compiler implicitly declares a
destructor. This implicitly declared destructor is an inline public member of its class.
The compiler will implicitly define an implicitly declared destructor when the compiler uses the
destructor to destroy an object of the destructor's class type. Suppose a class A has an implicitly
declared destructor. The following is equivalent to the function the compiler would implicitly define for
A:
A::~A() { }
The compiler first implicitly defines the implicitly declared destructors of the base classes and nonstatic
data members of a class A before defining the implicitly declared destructor of A
• It is implicitly defined
• All the direct base classes of A have trivial destructors
• The classes of all the nonstatic data members of A have trivial destructors
Class members that are class types can have their own destructors. Both base and derived classes
can have destructors, although destructors are not inherited. If a base class A or a member of A has a
destructor, and a class derived from A does not declare a destructor, a default destructor is generated.
The default destructor calls the destructors of the base class and members of the derived class.
The destructors of base classes and members are called in the reverse order of the completion of their
constructor:
1. The destructor for a class object is called before destructors for members and bases are
called.
2. Destructors for nonstatic members are called before destructors for base classes are called.
3. Destructors for nonvirtual base classes are called before destructors for virtual base classes
are called.
When an exception is thrown for a class object with a destructor, the destructor for the temporary
object thrown is not called until control passes out of the catch block.
Destructors are implicitly called when an automatic object (a local object that has been declared auto
or register, or not declared as static or extern) or temporary object passes out of scope. They
are implicitly called at program termination for constructed external and static objects. Destructors are
invoked when you use the delete operator for objects created with the new operator.
For example:
#include <string>
class Y {
private:
char * string;
int number;
public:
// Constructor
Y(const char*, int);
// Destructor
~Y() { delete[] string; }
};
int main () {
// Create and initialize
// object of class Y
Y yobj = Y("somestring", 10);
// ...
You can use a destructor explicitly to destroy objects, although this practice is not recommended.
However to destroy an object created with the placement new operator, you can explicitly call the
object's destructor. The following example demonstrates this:
#include <new>
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A::A()" << endl; }
~A() { cout << "A::~A()" << endl; }
};
int main () {
char* p = new char[sizeof(A)];
A* ap = new (p) A;
ap->A::~A();
delete [] p;
}
The statement A* ap = new (p) A dynamically creates a new object of type A not in the free store
but in the memory allocated by p. The statement delete [] p will delete the storage allocated by p,
but the run time will still believe that the object pointed to by ap still exists until you explicitly call the
destructor of A (with the statement ap->A::~A()).
Temporary objects are unnamed objects created on the stack by the compiler. They are used during
reference initialization and during evaluation of expressions including standard type conversions,
argument passing, function returns, and evaluation of the throw expression.
When a temporary object is created to initialize a reference variable, the name of the temporary object
has the same scope as that of the reference variable. When a temporary object is created during the
evaluation of a full-expression (an expression that is not a subexpression of another expression), it is
destroyed as the last step in its evaluation that lexically contains the point where it was created.
• The expression appears as an initializer for a declaration defining an object: the temporary
object is destroyed when the initialization is complete.
• A reference is bound to a temporary object: the temporary object is destroyed at the end of
the reference's lifetime.
If a temporary object is created for a class with constructors, the compiler calls the appropriate
(matching) constructor to create the temporary object.
When a temporary object is destroyed and a destructor exists, the compiler calls the destructor to
destroy the temporary object. When you exit from the scope in which the temporary object was
created, it is destroyed. If a reference is bound to a temporary object, the temporary object is
destroyed when the reference passes out of scope unless it is destroyed earlier by a break in the flow
of control. For example, a temporary object created by a constructor initializer for a reference member
is destroyed on leaving the constructor.
In cases where such temporary objects are redundant, the compiler does not construct them, in order
to create more efficient optimized code. This behavior could be a consideration when you are
debugging your programs, especially for memory problems.
Pseudo-destructor syntax
>>-+-+----+--+-----------------------+--type_name--::--~--
type_name-----------------+-><
| '-::-' '-nested_name_specifier-'
|
+-+----+--nested_name_specifier--template--template_identifier--::--~--
type_name-+
| '-::-'
|
'-+----+--+-----------------------+--~--
type_name--------------------------------'
'-::-' '-nested_name_specifier-'
The following example calls the pseudo destructor for an integer type:
typedef int I;
int main() {
I x = 10;
x.I::~I();
x = 20;
}
The call to the pseudo destructor, x.I::~I(), has no effect at all. Object x has not been destroyed;
the assignment x = 20 is still valid. Because pseudo destructors require the syntax for explicitly
calling a destructor for a nonclass type to be valid, you can write code without having to know whether
or not a destructor exists for a given type.
Default constructors (C++ only)
A default constructor is a constructor that either has no parameters, or if it has parameters, all the
parameters have default values.
If no user-defined constructor exists for a class A and one is needed, the compiler implicitly declares a
default parameterless constructor A::A(). This constructor is an inline public member of its class. The
compiler will implicitly define A::A() when the compiler uses this constructor to create an object of
type A. The constructor will have no constructor initializer and a null body.
The compiler first implicitly defines the implicitly declared constructors of the base classes and
nonstatic data members of a class A before defining the implicitly declared constructor of A. No default
constructor is created for a class that has any constant or reference type members.
• It is implicitly defined
• A has no virtual functions and no virtual base classes
• All the direct base classes of A have trivial constructors
• The classes of all the nonstatic data members of A have trivial constructors
Like all functions, a constructor can have default arguments. They are used to initialize member
objects. If default values are supplied, the trailing arguments can be omitted in the expression list of
the constructor. Note that if a constructor has any arguments that do not have default values, it is not a
default constructor.
A copy constructor for a class A is a constructor whose first parameter is of type A&, const A&,
volatile A&, or const volatile A&. Copy constructors are used to make a copy of one class
object from another class object of the same class type. You cannot use a copy constructor with an
argument of the same type as its class; you must use a reference. You can provide copy constructors
with additional parameters as long as they all have default arguments. If a user-defined copy
constructor does not exist for a class and one is needed, the compiler implicitly creates a copy
constructor, with public access, for that class. A copy constructor is not created for a class if any of its
members or base classes have an inaccessible copy constructor.
The following code fragment shows two classes with constructors, default constructors, and copy
constructors:
class X {
public:
// constructor
X(int, int , int = 0);
// copy constructor
X(const X&);
class Y {
public:
// default argument
// copy constructor
Y(const Y&, int = 0);
};
The copy constructor lets you create a new object from an existing one by initialization. A copy
constructor of a class A is a non-template constructor in which the first parameter is of type A&, const
A&, volatile A&, or const volatile A&, and the rest of its parameters (if there are any) have
default values.
If you do not declare a copy constructor for a class A, the compiler will implicitly declare one for you,
which will be an inline public member.
The following example demonstrates implicitly defined and user-defined copy constructors:
#include <iostream>
using namespace std;
struct A {
int i;
A() : i(10) { }
};
struct B {
int j;
B() : j(20) {
cout << "Constructor B(), j = " << j << endl;
}
struct C {
C() { }
C(C&) { }
};
int main() {
A a;
A a1(a);
B b;
const B b_const;
B b1(b);
B b2(b_const);
const C c_const;
// C c1(c_const);
}
Constructor B(), j = 20
Constructor B(), j = 20
Copy constructor B(B&), j = 20
Copy constructor B(const B&, int), j = 30
The statement A a1(a) creates a new object from a with an implicitly defined copy constructor. The
statement B b1(b) creates a new object from b with the user-defined copy constructor B::B(B&).
The statement B b2(b_const) creates a new object with the copy constructor B::B(const B&,
int). The compiler would not allow the statement C c1(c_const) because a copy constructor that
takes as its first parameter an object of type const C& has not been defined.
The implicitly declared copy constructor of a class A will have the form A::A(const A&) if the
following are true:
• The direct and virtual bases of A have copy constructors whose first parameters have been
qualified with const or const volatile
• The nonstatic class type or array of class type data members of A have copy constructors
whose first parameters have been qualified with const or const volatile
If the above are not true for a class A, the compiler will implicitly declare a copy constructor with the
form A::A(A&).
The compiler cannot allow a program in which the compiler must implicitly define a copy constructor for
a class A and one or more of the following are true:
• Class A has a nonstatic data member of a type which has an inaccessible or ambiguous copy
constructor.
• Class A is derived from a class which has an inaccessible or ambiguous copy constructor.
The compiler will implicitly define an implicitly declared constructor of a class A if you initialize an
object of type A or an object derived from class A.
An implicitly defined copy constructor will copy the bases and members of an object in the same order
that a constructor would initialize the bases and members of the object.
Like other member functions, constructors and destructors are declared within a class declaration.
They can be defined inline or external to the class declaration. Constructors can have default
arguments. Unlike other member functions, constructors can have member initialization lists. The
following restrictions apply to constructors and destructors:
• Constructors and destructors do not have return types nor can they return values.
• References and pointers cannot be used on constructors and destructors because their
addresses cannot be taken.
• Constructors cannot be declared with the keyword virtual.
• Constructors and destructors cannot be declared static, const, or volatile.
• Unions cannot contain class objects that have constructors or destructors.
Constructors and destructors obey the same access rules as member functions. For example, if you
declare a constructor with protected access, only derived classes and friends can use it to create class
objects.
The compiler automatically calls constructors when defining class objects and calls destructors when
class objects go out of scope. A constructor does not allocate memory for the class object its this
pointer refers to, but may allocate storage for more objects than its class object refers to. If memory
allocation is required for objects, constructors can explicitly call the new operator. During cleanup, a
destructor may release objects allocated by the corresponding constructor. To release objects, use the
delete operator.
Derived classes do not inherit or overload constructors or destructors from their base classes, but they
do call the constructor and destructor of base classes. Destructors can be declared with the keyword
virtual.
Constructors are also called when local or temporary class objects are created, and destructors are
called when local or temporary objects go out of scope.
You can call member functions from constructors or destructors. You can call a virtual function, either
directly or indirectly, from a constructor or destructor of a class A. In this case, the function called is the
one defined in A or a base class of A, but not a function overridden in any class derived from A. This
avoids the possibility of accessing an unconstructed object from a constructor or destructor. The
following example demonstrates this:
#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "void A::f()" << endl; }
virtual void g() { cout << "void A::g()" << endl; }
virtual void h() { cout << "void A::h()" << endl; }
};
struct B : A {
virtual void f() { cout << "void B::f()" << endl; }
B() {
f();
g();
h();
}
};
struct C : B {
virtual void f() { cout << "void C::f()" << endl; }
virtual void g() { cout << "void C::g()" << endl; }
virtual void h() { cout << "void C::h()" << endl; }
};
int main() {
C obj;
}
void B::f()
void A::g()
void A::h()
The constructor of B does not call any of the functions overridden in C because C has been derived
from B, although the example creates an object of type C named obj.
You can use the typeid or the dynamic_cast operator in constructors or destructors, as well as
member initializers of constructors.
In some cases, the exception handling mechanism fails and a call to void terminate() is made.
This terminate() call occurs in any of the following situations:
• The exception handling mechanism cannot find a handler for a thrown exception. The
following are more specific cases of this:
o During stack unwinding, a destructor throws an exception and that exception is not
handled.
o The expression that is thrown also throws an exception, and that exception is not
handled.
o The constructor or destructor of a nonlocal static object throws an exception, and the
exception is not handled.
o A function registered with atexit() throws an exception, and the exception is not
handled. The following demonstrates this:
o extern "C" printf(char* ...);
o #include <exception>
o #include <cstdlib>
o using namespace std;
o
o void f() {
o printf("Function f()\n");
o throw "Exception thrown from f()";
o }
o
o void g() { printf("Function g()\n"); }
o void h() { printf("Function h()\n"); }
o
o void my_terminate() {
o printf("Call to my_terminate\n");
o abort();
o }
o
o int main() {
o set_terminate(my_terminate);
o atexit(f);
o atexit(g);
o atexit(h);
o printf("In main\n");
}
The following is the output of the above example:
In main
Function h()
Function g()
Function f()
Call to my_terminate
To register a function with atexit(), you pass a parameter to atexit() a pointer
to the function you want to register. At normal program termination, atexit() calls
the functions you have registered with no arguments in reverse order. The atexit()
function is in the <cstdlib> library.
• A throw expression without an operand tries to rethrow an exception, and no exception is
presently being handled.
• A function f() throws an exception that violates its exception specification. The
unexpected() function then throws an exception which violates the exception specification
of f(), and the exception specification of f() did not include the class
std::bad_exception.
• The default value of unexpected_handler is called.
A terminate function cannot return to its caller, either by using return or by throwing an exception.