Saturday, 27 August 2011

C++ Interview Questions and Answers (4)

C++ Interview Questions and Answers (4)

Pointers

Q: What is a dangling pointer?
A: A dangling pointer arises when you use the address of an object after its lifetime is over. This may occur in situations like returning addresses of the automatic variables from a function or using the address of the memory block after it is freed.

Q: What is Memory Leak?
A: Memory which has no pointer pointing to it and there is no way to delete or reuse this memory(object), it causes Memory leak.
{
Base *b = new base();
}
Out of this scope b no longer exists, but the memory it was pointing to was not deleted. Pointer b
itself was destroyed when it went out of scope.

Q: What is auto pointer?
A: The simplest example of a smart pointer is auto_ptr, which is included in the standard C++
library. Auto Pointer only takes care of Memory leak and does nothing about dangling pointers
issue. You can find it in the header . Here is part of auto_ptr's implementation, to illustrate what
it does:


template class auto_ptr
{
T* ptr;
public:
explicit auto_ptr(T* p = 0) : ptr(p) {}
~auto_ptr() {delete ptr;}
T& operator*() {return *ptr;}
T* operator->() {return ptr;}
// ...
};



As you can see, auto_ptr is a simple wrapper around a regular pointer. It forwards all meaningful
operations to this pointer (dereferencing and indirection). Its smartness in the destructor: the
destructor takes care of deleting the pointer.
For the user of auto_ptr, this means that instead of writing:


void foo()
{
MyClass* p(new MyClass);
p->DoSomething();

delete p;
}


You can write:
void foo()
{
auto_ptr p(new MyClass);
p->DoSomething();
}

And trust p to cleanup after itself.
Q: What issue do auto_ptr objects address?
A: If you use auto_ptr objects you would not have to be concerned with heap objects not being
deleted even if the exception is thrown.
Q: What is a smart pointer?
A: A smart pointer is a C++ class that mimics a regular pointer in syntax and some semantics,
but it does more. Because smart pointers to different types of objects tend to have a lot of code in
common, almost all good-quality smart pointers in existence are templated by the pointee type,
as you can see in the following code:


template
class SmartPtr
{
public:
explicit SmartPtr(T* pointee) : pointee_(pointee);
SmartPtr& operator=(const SmartPtr& other);
~SmartPtr();
T& operator*() const
{
...
return *pointee_;
}
T* operator->() const
{
...
return pointee_;
}
private:
T* pointee_;
...
};


SmartPtr aggregates a pointer to T in its member variable pointee_. That's what most smart
pointers do. In some cases, a smart pointer might aggregate some handles to data and compute
the pointer on the fly.
The two operators give SmartPtr pointer-like syntax and semantics. That is, you can write


class Widget
{
public:
void Fun();
};

SmartPtr sp(new Widget);
sp->Fun();
(*sp).Fun();



Aside from the definition of sp, nothing reveals it as not being a pointer. This is the mantra of
smart pointers: You can replace pointer definitions with smart pointer definitions without
incurring major changes to your application's code. You thus get extra goodies with ease.
Minimizing code changes is very appealing and vital for getting large applications to use smart
pointers. As you will soon see, however, smart pointers are not a free lunch.

Q: Is there any problem with the following : char*a=NULL; char& p = *a;?
A: The result is undefined. You should never do this. A reference must always refer to some
object.

Q: What is the difference between a pointer and a reference?
A: A reference must always refer to some object and, therefore, must always be initialized;
pointers do not have such restrictions. A pointer can be reassigned to point to different objects
while a reference always refers to an object with which it was initialized.

Q: What is the difference between const char *myPointer and char *const myPointer?
A: Const char *myPointer is a non constant pointer to constant data; while char *const
myPointer is a constant pointer to non constant data.

Q:When should I use references, and when should I use pointers?
A: Use references when you can, and pointers when you have to.

References are usually preferred over pointers whenever you don't need "reseating". This usually
means that references are most useful in a class's public interface. References typically appear on
the skin of an object, and pointers on the inside.

The exception to the above is where a function's parameter or return value needs a "sentinel"
reference a reference that does not refer to an object. This is usually best done by

returning/taking a pointer, and giving the NULL pointer this special significance (references
should always alias objects, not a dereferenced NULL pointer).

Note: Old line C programmers sometimes don't like references since they provide reference
semantics that isn't explicit in the caller's code. After some C++ experience, however, one
quickly realizes this is a form of information hiding, which is an asset rather than a liability. E.g.,
programmers should write code in the language of the problem rather than the language of the
machine.

String


Q: What happens if you write this code?

string& foo()
{
return "Hello World";
}

cout << foo() << endl; A: 1. Will give an error since Hello World is created as a unnamed character pointer to const. it is being assigned to non-const reference which is not allowed. could not convert `"Hello World"' to `std::string&' 2. const string& foo1() { return "Hello World"; } Gives a warning, since you are returning a reference to temporary, which will die immediately when the expression is completed. classsize.C:7: warning: returning reference to temporary output : Aborted. Segment fault. 3. Char *foo1() { return “Hello World”; } Returning the address of character literal which is created on the static memory. In C++, the compiler allows the use of string literals to initialize character arrays. A string literal consists of zero or more characters surrounded by double quotation marks ("). A string literal represents a sequence of characters that, taken together, form a null-terminated string. The compiler creates static storage space for the string, null-terminates it, and puts the address of this space into the char* variable. The type of a literal string is an array of const chars. char* szMyString = "Hello world."; szMyString[3] = 'q'; // undefined, modifying static buffer!!! In the following example, the compiler automatically puts a null-character at the end of the literal string of characters "Hello world". It then creates a storage space for the resulting string - this is an array of const chars. Then it puts the starting address of this array into the szMyString variable. We will try to modify this string (wherever it is stored) by accessing it via an index into szMyString. This is a Bad Thing; the standard does not say where the compiler puts literal strings. They can go anywhere, possibly in some place in memory that you shouldn't be modifying. Q: How do I convert an integer to a string? A: The simplest way is to use a stringstream: #include #include #include using namespace std; string itos(int i) // convert int to string { stringstream s; s << i; return s.str(); } int main() { int i = 127; string ss = itos(i); const char* p = ss.c_str(); cout << ss << " " << p << "\n"; } Naturally, this technique works for converting any type that you can output using << to a string. C vs C++ Q: How do you link a C++ program to C functions? A: By using the extern "C" linkage specification around the C function declarations. Programmers should know about mangled function names and type-safe linkages. Then they should explain how the extern "C" linkage specification statement turns that feature off during compilation so that the linker properly links function calls to C functions. Q: Is there anything you can do in C++ that you cannot do in C? A: No. There is nothing you can do in C++ that you cannot do in C. After all you can write a C++ compiler in C Q: What are the differences between a struct in C and in C++? A: In C++ a struct is similar to a class except for the default access specifier( refere to other question in the document). In C we have to include the struct keyword when declaring struct. In c++ we don’t have to. Q: What does extern "C" int func(int *, Foo) accomplish? A: It will turn o_ "name mangling" for func so that one can link to code compiled by a C compiler. Q: What are the access privileges in C++? What is the default access level? A: The access privileges in C++ are private, public and protected. The default access level assigned to members of a class is private. Private members of a class are accessible only within the class and by friends of the class. Protected members are accessible by the class itself and it's sub-classes. Public members of a class can be accessed by anyone. Q:How does C++ help with the tradeoff of safety vs. usability? A: In C, encapsulation was accomplished by making things static in a compilation unit or module. This prevented another module from accessing the static stuff. (By the way, static data at file-scope is now deprecated in C++: don't do that.) Unfortunately this approach doesn't support multiple instances of the data, since there is no direct support for making multiple instances of a module's static data. If multiple instances were needed in C, programmers typically used a struct. But unfortunately C structs don't support encapsulation. This exacerbates the tradeoff between safety (information hiding) and usability (multiple instances). In C++, you can have both multiple instances and encapsulation via a class. The public part of a class contains the class's interface, which normally consists of the class's public member functions and its friend functions. The private and/or protected parts of a class contain the class's implementation, which is typically where the data lives. The end result is like an "encapsulated struct." This reduces the tradeoff between safety (information hiding) and usability (multiple instances). Overloading operator Q: Name the operators that cannot be overloaded? A:sizeof, ., .*, .->, ::, ?:

Q: What is overloading??
A: With the C++ language, you can overload functions and operators. Overloading is the
practice of supplying more than one definition for a given function name in the same scope.
- Any two functions in a set of overloaded functions must have different argument lists.
- Overloading functions with argument lists of the same types, based on return type alone, is an
error.

Q: How are prefix and postfix versions of operator++() differentiated?
A: The postfix version of operator++() has a dummy parameter of type int. The prefix version
does not have dummy parameter.

Q: Can you overload a function based only on whether a parameter is a value or a reference?
A: No. Passing by value and by reference looks identical to the caller.
Q: What's the deal with operator overloading?
A: It allows you to provide an intuitive interface to users of your class, plus makes it possible for
templates to work equally well with classes and built-in/intrinsic types.

Operator overloading allows C/C++ operators to have user-defined meanings on user-defined
types (classes). Overloaded operators are syntactic sugar for function calls:


class Fred {
public:
...
};

#if 0

// Without operator overloading:
Fred add(const Fred& x, const Fred& y);
Fred mul(const Fred& x, const Fred& y);

Fred f(const Fred& a, const Fred& b, const Fred& c)
{
return add(add(mul(a,b), mul(b,c)), mul(c,a)); // Yuk...
}

#else

// With operator overloading:

Fred operator+ (const Fred& x, const Fred& y);
Fred operator* (const Fred& x, const Fred& y);

Fred f(const Fred& a, const Fred& b, const Fred& c)
{
return a*b + b*c + c*a;
}

#endif

Q: What are the benefits of operator overloading?
A: By overloading standard operators on a class, you can exploit the intuition of the users of that
class. This lets users program in the language of the problem domain rather than in the language
of the machine.

The ultimate goal is to reduce both the learning curve and the defect rate.

Q: What are some examples of operator overloading?
A: Here are a few of the many examples of operator overloading:

myString + yourString might concatenate two std::string objects
myDate++ might increment a Date object
a * b might multiply two Number objects
a[i] might access an element of an Array object
x = *p might dereference a "smart pointer" that "points" to a disk record it could seek to the
location on disk where p "points" and return the appropriate record into x

Q: But operator overloading makes my class look ugly; isn't it supposed to make my code
clearer?
A: Operator overloading makes life easier for the users of a class, not for the developer of the
class!

Consider the following example.


class Array {
public:
int& operator[] (unsigned i); // Some people don't like this syntax
...
};

inline
int& Array::operator[] (unsigned i) // Some people don't like this syntax
{
...
}

Some people don't like the keyword operator or the somewhat funny syntax that goes with it in
the body of the class itself. But the operator overloading syntax isn't supposed to make life easier
for the developer of a class. It's supposed to make life easier for the users of the class:


int main()
{
Array a;
a[3] = 4; // User code should be obvious and easy to understand...
...
}
Remember: in a reuse-oriented world, there will usually be many people who use your class, but
there is only one person who builds it (yourself); therefore you should do things that favor the
many rather than the few.

Q: What operators can/cannot be overloaded?
A: Most can be overloaded. The only C operators that can't be are . and ?: (and sizeof, which is
technically an operator). C++ adds a few of its own operators, most of which can be overloaded
except :: and .*.

Here's an example of the subscript operator (it returns a reference). First without operator
overloading:


class Array {
public:
int& elem(unsigned i) { if (i > 99) error(); return data[i]; }
private:
int data[100];
};

int main()
{
Array a;
a.elem(10) = 42;
a.elem(12) += a.elem(13);
...
}
Now the same logic is presented with operator overloading:


class Array {
public:
int& operator[] (unsigned i) { if (i > 99) error(); return data[i]; }
private:
int data[100];

};

int main()
{
Array a;
a[10] = 42;
a[12] += a[13];
...
}

Q: Can I overload operator== so it lets me compare two char[] using a string comparison?
A: No: at least one operand of any overloaded operator must be of some user-defined type (most
of the time that means a class).

But even if C++ allowed you to do this, which it doesn't, you wouldn't want to do it anyway
since you really should be using a std::string-like class rather than an array of char in the first
place since arrays are evil.

Q: Can I create a operator** for "to-the-power-of" operations?
A: Nope.

The names of, precedence of, associativity of, and arity of operators is fixed by the language.
There is no operator** in C++, so you cannot create one for a class type.

If you're in doubt, consider that x ** y is the same as x * (*y) (in other words, the compiler
assumes y is a pointer). Besides, operator overloading is just syntactic sugar for function calls.
Although this particular syntactic sugar can be very sweet, it doesn't add anything fundamental. I
suggest you overload pow(base,exponent) (a double precision version is in ).

By the way, operator^ can work for to-the-power-of, except it has the wrong precedence and
associativity.

Q: Okay, that tells me the operators I can override; which operators should I override?
A: Bottom line: don't confuse your users.

Remember the purpose of operator overloading: to reduce the cost and defect rate in code that
uses your class. If you create operators that confuse your users (because they're cool, because
they make the code faster, because you need to prove to yourself that you can do it; doesn't really
matter why), you've violated the whole reason for using operator overloading in the first place.

Q: What are some guidelines / "rules of thumb" for overloading operators?
A: Here are a few guidelines / rules of thumb (but be sure to read the previous FAQ before
reading this list):

Use common sense. If your overloaded operator makes life easier and safer for your users, do it;
otherwise don't. This is the most important guideline. In fact it is, in a very real sense, the only

guideline; the rest are just special cases.
If you define arithmetic operators, maintain the usual arithmetic identities. For example, if your
class defines x + y and x - y, then x + y - y ought to return an object that is behaviorally
equivalent to x. The term behaviorally equivalent is defined in the bullet on x == y below, but
simply put, it means the two objects should ideally act like they have the same state. This should
be true even if you decide not to define an == operator for objects of your class.
You should provide arithmetic operators only when they make logical sense to users. Subtracting
two dates makes sense, logically returning the duration between those dates, so you might want
to allow date1 - date2 for objects of your Date class (provided you have a reasonable class/type
to represent the duration between two Date objects). However adding two dates makes no sense:
what does it mean to add July 4, 1776 to June 5, 1959? Similarly it makes no sense to multiply or
divide dates, so you should not define any of those operators.
You should provide mixed-mode arithmetic operators only when they make logical sense to
users. For example, it makes sense to add a duration (say 35 days) to a date (say July 4, 1776), so
you might define date + duration to return a Date. Similarly date - duration could also return a
Date. But duration - date does not make sense at the conceptual level (what does it mean to
subtract July 4, 1776 from 35 days?) so you should not define that operator.
If you provide constructive operators, they should return their result by value. For example, x + y
should return its result by value. If it returns by reference, you will probably run into lots of
problems figuring out who owns the referent and when the referent will get destructed. Doesn't
matter if returning by reference is more efficient; it is probably wrong. See the next bullet for
more on this point.
If you provide constructive operators, they should not change their operands. For example, x + y
should not change x. For some crazy reason, programmers often define x + y to be logically the
same as x += y because the latter is faster. But remember, your users expect x + y to make a
copy. In fact they selected the + operator (over, say, the += operator) precisely because they
wanted a copy. If they wanted to modify x, they would have used whatever is equivalent to x +=
y instead. Don't make semantic decisions for your users; it's their decision, not yours, whether
they want the semantics of x + y vs. x += y. Tell them that one is faster if you want, but then step
back and let them make the final decision they know what they're trying to achieve and you do
not.
If you provide constructive operators, they should allow promotion of the left-hand operand. For
example, if your class Fraction supports promotion from int to Fraction (via the non-explicit ctor
Fraction::Fraction(int)), and if you allow x - y for two Fraction objects, you should also allow 42
- y. In practice that simply means that your operator-() should not be a member function of
Fraction. Typically you will make it a friend, if for no other reason than to force it into the
public: part of the class, but even if it is not a friend, it should not be a member.
In general, your operator should change its operand(s) if and only if the operands get changed
when you apply the same operator to intrinsic types. x == y and x << y should not change either operand; x *= y and x <<= y should (but only the left-hand operand). If you define x++ and ++x, maintain the usual identities. For example, x++ and ++x should have should have the same observable effect on x, and should differ only in what they return. ++x should return x by reference; x++ should either return a copy (by value) of the original state of x or should have a void return-type. You're usually better off returning a copy of the original state of x by value, especially if your class will be used in generic algorithms. The easy way to do that is to implement x++ using three lines: make a local copy of *this, call ++x (i.e., this- >operator++()), then return the local copy. Similar comments for x-- and --x.
If you define ++x and x += 1, maintain the usual identities. For example, these expressions
should have the same observable behavior, including the same result. Among other things, that
means your += operator should return x by reference. Similar comments for --x and x -= 1.
If you define *p and p[0] for pointer-like objects, maintain the usual identities. For example,
these two expressions should have the same result and neither should change p.
If you define p[i] and *(p+i) for pointer-like objects, maintain the usual identities. For example,
these two expressions should have the same result and neither should change p. Similar
comments for p[-i] and *(p-i).
Subscript operators generally come in pairs; see on const-overloading.
If you define x == y, then x == y should be true if and only if the two objects are behaviorally
equivalent. In this bullet, the term "behaviorally equivalent" means the observable behavior of
any operation or sequence of operations applied to x will be the same as when applied to y. The
term "operation" means methods, friends, operators, or just about anything else you can do with
these objects (except, of course, the address-of operator). You won't always be able to achieve
that goal, but you ought to get close, and you ought to document any variances (other than the
address-of operator).
If you define x == y and x = y, maintain the usual identities. For example, after an assignment,
the two objects should be equal. Even if you don't define x == y, the two objects should be
behaviorally equivalent (see above for the meaning of that phrase) after an assignment.
If you define x == y and x != y, you should maintain the usual identities. For example, these
expressions should return something convertible to bool, neither should change its operands, and
x == y should have the same result as !(x != y), and vice versa.
If you define inequality operators like x <= y and x < y, you should maintain the usual identities. For example, if x < y and y < z are both true, then x < z should also be true, etc. Similar comments for x >= y and x > y.
If you define inequality operators like x < y and x >= y, you should maintain the usual identities.
For example, x < y should have the result as !(x >= y). You can't always do that, but you should
get close and you should document any variances. Similar comments for x > y and !(x <= y), etc.
Avoid overloading short-circuiting operators: x || y or x && y. The overloaded versions of these
do not short-circuit they evaluate both operands even if the left-hand operand "determines" the
outcome, so that confuses users.
Avoid overloading the comma operator: x, y. The overloaded comma operator does not have the
same ordering properties that it has when it is not overloaded, and that confuses users.
Don't overload an operator that is non-intuitive to your users. This is called the Doctrine of Least
Surprise. For example, altough C++ uses std::cout << x for printing, and although printing is
techincally called inserting, and although inserting sort of sounds like what happens when you
push an element onto a stack, don't overload myStack << x to push an element onto a stack. It
might make sense when you're really tired or otherwise mentally impaired, and a few of your
friends might think it's "kewl," but just say No.
Use common sense. If you don't see "your" operator listed here, you can figure it out. Just
remember the ultimate goals of operator overloading: to make life easier for your users, in
particular to make their code cheaper to write and more obvious.
Caveat: the list is not exhaustive. That means there are other entries that you might consider
"missing." I know.

No comments:

Post a Comment