1.
If you define a class like this:
class X: public Y
{
private:
int m_a;
char* m_b;
Z m_c;
};
The following methods will be defined by your compiler.
- Constructor (default) (2 versions)
- Constructor (Copy)
- Destructor (default)
- Assignment operator
Constructor: Default:
There are actually two default constructors.
One is used for zero-initialization
while the other is used for value-initialization
. The used depends on whether you use ()
during initialization or not.
// Zero-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Zero-Initialization version'
,m_a(0) // Default construction of basic PODS zeros them
,m_b(0) //
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Zero-Initialization version'
{
}
// Value-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Value-Initialization version'
//,m_a() // Default construction of basic PODS does nothing
//,m_b() // The values are un-initialized.
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Value-Initialization version'
{
}
Notes: If the base class or any members do not have a valid visible default constructor then the default constructor can not be generated. This is not an error unless your code tries to use the default constructor (then only a compile time error).
Constructor (Copy)
X::X(X const& copy)
:Y(copy) // Calls the base copy constructor
,m_a(copy.m_a) // Calls each members copy constructor
,m_b(copy.m_b)
,m_c(copy.m_c)
{}
Notes: If the base class or any members do not have a valid visible copy constructor then the copy constructor can not be generated. This is not an error unless your code tries to use the copy constructor (then only a compile time error).
Assignment Operator
X& operator=(X const& copy)
{
Y::operator=(copy); // Calls the base assignment operator
m_a = copy.m_a; // Calls each members assignment operator
m_b = copy.m_b;
m_c = copy.m_c;
return *this;
}
Notes: If the base class or any members do not have a valid viable assignment operator then the assignment operator can not be generated. This is not an error unless your code tries to use the assignment operator (then only a compile time error).
Destructor
X::~X()
{
// First runs the destructor code
}
// This is psudo code.
// But the equiv of this code happens in every destructor
m_c.~Z(); // Calls the destructor for each member
// m_b // PODs and pointers destructors do nothing
// m_a
~Y(); // Call the base class destructor
- If any constructor (including copy) is declared then the default constructor is not implemented by the compiler.
- If the copy constructor is declared then the compiler will not generate one.
- If the assignment operator is declared then the compiler will not generate one.
- If a destructor is declared the compiler will not generate one.
Looking at your code the following copy constructors are generated:
Foo::Foo(Foo const& copy)
:bar(copy.bar)
{}
Bar::Bar(Bar const& copy)
:i(copy.i)
,baz(copy.baz)
{}
Baz::Baz(Baz const& copy)
:j(copy.j)
{}
2. n C++ struct
is (almost) synonymous to a class
(except of different default access level). So a class can inherit from a struct.
3. A a = b; //call copy constructor instead of constructor + assignment operator
4. Virtual Inheritance:
class AbsBase {...};
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};
Basically, the default, non-virtual multiple inheritance will include a copy of each base class in the derived class, and includes all their methods. This is why you have two copies of AbsBase -- and the reason your method use is ambiguous is both sets of methods are loaded, so C++ has no way to know which copy to access!
Virtual inheritance condenses all references to a virtual base class into one datastructure. This should make the methods from the base class unambiguous again. However, note: if there is additional data in the two intermediate classes, there may be some small additional runtime overhead, to enable the code to find the shared virtual base class.
5. Allocate two dimension array using calloc
Type 1:
double **array1;
int ii;
array1 = calloc(10, sizeof(double *));
for(ii = 0; ii < 10; ii++) {
array1[ii] = calloc(10, sizeof(double));
}
// Then access array elements like array1[ii][jj]
Type 2:
double **array1;
int ii;
array1 = calloc(10 * 10, sizeof(double *));
// Then access array elements like array1[ii + 10*jj]
Type 3:
double **array1;
int ii;
array1 = malloc(10 * 10, sizeof(double *));
// Then access array elements like array1[ii + 10*jj]
6. Default Catch Statement, compile error if it is not at the last
try {
// code here
}
catch (int param) { cout << "int exception"; }
catch (char param) { cout << "char exception"; }
catch (...) { cout << "default exception"; }
7. Exception specification
class A
{
public:
virtual void f() throw ( int ) { }
};
class B: public A
{
public:
void f() throw ( int, double ) { } // compile error, derived exception specification must be at least as strict
};
8. Inline Functions:
virtual function can't be inline because they won't be evaluated until runtime
Advantages :-
1) It does not require function calling overhead.
2) It also save overhead of variables push/pop on the stack, while function calling.
3) It also save overhead of return call from a function.
4) It increases locality of reference by utilizing instruction cache.
5) After in-lining compiler can also apply intraprocedural optmization if specified. This is the most important one, in this way compiler can now focus on dead code elimination, can give more stress on branch prediction, induction variable elimination etc..
Disadvantages :-
1) May increase function size so that it may not fit on the cache, causing lots of cahce miss.
2) After in-lining function if variables number which are going to use register increases than they may create overhead on register variable resource utilization.
3) It may cause compilation overhead as if some body changes code inside inline function than all calling location will also be compiled.
4) If used in header file, it will make your header file size large and may also make it unreadable.
5) If somebody used too many inline function resultant in a larger code size than it may cause thrashing in memory. More and more number of page fault bringing down your program performance.
6) Its not useful for embeded system where large binary size is not preferred at all due to memory size constraints.
9. Inline vs Macro
Now, as far as using macros vs. inline functions in a function-like context, be advised that:
- Macros are not type safe, and can be expanded regardless of whether they are syntatically correct - the compile phase will report errors resulting from macro expansion problems.
- Macros can be used in context where you don't expect, resulting in problems
- Macros are more flexible, in that they can expand other macros - whereas inline functions don't necessarily do this.
- Macros can result in side effects because of their expansion, since the input expressions are copied wherever they appear in the pattern.
- Inline function are not always guaranteed to be inlined - some compilers only do this in release builds, or when they are specifically configured to do so. Also, in some cases inlining may not be possible.
- Inline functions can provide scope for variables (particularly static ones), preprocessor macros can only do this in code blocks {...}, and static variables will not behave exactly the same way.
10. Compiling process
11. Malloc vs Calloc
void* malloc (size_t size);
Allocates a block of size bytesof memory, returning a pointer to the beginning of the block. If the function failed to allocate the requested block of memory, a null pointer is returned.
void* calloc (size_t num, size_t size);
Allocates a block of memory for an array of num elements, each of them size bytes long, andinitializes all its bits to zero.
12. auto_ptr vs shared_ptr
auto_ptr
is a thin wrapper around pointers to implement RAII semantics, so that resources are always released, even when facing exceptions. auto_ptr
does not perform any reference counting or the like at all, it does not make multiple pointers point to the same object when creating copies. In fact, it's very different. auto_ptr
is one of the few classes where the assignment operator modifies the source object. Consider this shameless plug from the auto_ptr wikipedia page:
int *i = new int;
auto_ptr<int> x(i);
auto_ptr<int> y;
y = x;
cout << x.get() << endl; // Print NULL
cout << y.get() << endl; // Print non-NULL address i
13.
std::nothrow
// nothrow example
#include <iostream> // std::cout
#include <new> // std::nothrow
int main () {
std::cout << "Attempting to allocate 1 MiB...";
char* p = new (std::nothrow) char [1048576];
if (p==0) std::cout << "Failed!\n";
else {
std::cout << "Succeeded!\n";
delete[] p;
}
return 0;
}
16.
Dynamic and static Scoping program
int x;
int main() {
x = 14;
f();
g();
}
void f() {
int x = 13;
h();
}
void g() {
int x = 12;
h();
}
void h() {
printf("%d\n",x);
}
Static scoping means that x
refers to the x
declared innermost scope of declaration that has one. Since h
is declared inside the global scope, the innermost x
is the one in the global scope(it has no access to the x
s in f
and g
, since it was not declared inside them), so the program prints 14
twice.
Dynamic scoping means that x
refers to the x
declared in the most recent frame of the call-stackthe has one. If C used dynamic scoping, h
would use the x
from either f
or g
- whichever one that called it - so the program would print 13
and 12
.
17.
| s = "Hello world!" The size of main()'s (s) is 13 The size of f()'s (s) is 4 The size of main()'s (&s) is 4 |
18. Initialize array or vector in constructor
option 1:
class TrieNode {
public:
// Initialize your data structure here.
TrieNode() : child({NULL}), child(26, NULL)
{
flag = false;
}
bool flag;
TrieNode* child[26];
vector<TrieNode *> child;
};
option 2:
class TrieNode {
public:
// Initialize your data structure here.
TrieNode()
{
memset(child, 0, sizeof(child));
flag = false;
}
bool flag;
TrieNode* child[26];
};
use struct
structTrieNode {
bool flag;
TrieNode* child[26];
vector<TrieNode *> child(26, NULL);
};