Constructor:
Class X
{
int i;
public:
X();
}
void f() is
{
X a;
}
the same thing happen as if a were an int. Storage is allocated for object But when the program reaches the sequence point(point of execution) where a is defined, the constructor is called automatically. That is, the compiler quietly insert the
call to X::X() for the object a at point of definition. like any member function the first secret argument to the constructor is the this pointer the address of the object for which it is being called .in the case of constructor however this is pointing to an unintialized block of memory and it is job of the constructor to intialize this memory properly.
If u have constructor with the argument than you can initialize object with argument. Tree a(2).
Constructor and destructor can not have return value.
In the Destructor never has any argument because destruction never need any option.
Class Y {
Public:
~Y();
};
Example of constructor and destructors
#include<iostream>
Using namespace std;
Class Tree
{
Int height;
Public :
Tree( initialHeight);
~Tree();
Void grow(int years);
Void printsize();
Void grow(int years);
Void printsize();
};
like any function, the constructor can have argument to allow you to specify how an object is created.
A default constructor is one that can be called with no argument.
Storage allocation:
A variable can now be defined at any point in a scope. So it might seem that the storage for the variable may not be defined until its point of definition.
The storage is allocated at the beginning begining of the block, the constructor
Call does not happen until the sequence point where the object is defined because the identifier is not available until then.
Aggregate initialization
An aggregate is just what it sounds like : a bunch of things clumped together.
This definition includes aggregates of mixed types like struct and classes. An array is an aggregate of a single type.
When you create an object that’s an aggregate all you must do is make assignment and the initialization will be taken care of by the compiler.
This assignment can be in several flavors For an array of built in type this is quite simple
Int a[5] ={1,2,3,4,5}
If u try to give more initializers than there are array element , the compiler gives an error message.
Int b[6] ={0}
A second shorthand for arrays is automatic counting in which you let the compiler
Determine the size of the array based on the number of initializers.
Int c[] ={ 1,2,3,4 }
Now if you decide to add another element to the array you simply add another initialize.
The size of array will be sizeof c /sizeof *c ( size of the entire array deivided by the size of the first element)
For(int I =0; i< sizeof c/ sizeof *c ;i++)
C[i]++;
Structure are also aggregates , they can be initialized in a similar fashion. Because a C style struct has all of its members.
Struct X
{
Int I;
Float f;
Char c;
};
X x1 ={1,2.2.’c’}
If you have an array of such object you can initialize them by using a nested set of curly braces for each object.
X x2[3] ={ {1,1.1,’a’} , {2,2.2.,’b’}}
Here the third object is initialized to zero.
If you have struct with all members public or a class with private data member all the initialization must go through the constructor even if you are using aggregat Initialization.
Second example showing multiple constructor argument.
#include<iostream>
Using namespace std;
Class z {
Int I,j;
Public:
Z(int ii ,int jj);
Void print ();
};
Z::Z(int ii , int jj)
{
I =ii;
J=jj;
}
Void Z::print() {
Cout<<” I =”<<I <<” j=”<<j<<endl;
}
Int main()
{
Z zz[] = { Z(1,2) ,Z(3,4) , Z(5,6) ,Z(7,8) };
For(int I =0 ; i< sizeof zz / sizeof *zz ; i++)
Zz[i].print();
Notice that it look like an explicit constructor is called for each object in the array.
}
Default constructor:
A default constructor is one that can be called with no argument. A default constructor is used to create a “vanilla object” but it is also important when the
Compiler is told to create an object but is not given details.
For Example if you take the struct Y defined previously and use it in a definition like this .
Y y2[2] ={ Y(1) };
The complier will complain that it can not find a default constructor.
The second object in the array wants to be created with no argument
And that’s where the compiler looks for a default constructor.
In fact if you simply define an array of objects
Y y3[7];
The complier will complain because it must have a default constructor to initialize every object in the array.
The default constructor is so important if and only if there are no constructor for a constructor
Function overloading and default argument
More name decoration
The concept of name decoration was introduced.
Void f()
Class X
{
Void f();
}
The function f() inside the scope of class X does not clash with the global version of f().
The compiler perform this scoping by the manufacturing different internal name for the global version of f()and X::f().
It was suggested that the name are simply the class name “decorated” together
Function name so the internal names the compiler uses might be _f and _X_f.
The compiler will give the name with the argument list with function name. it does not generate compiler name only with the function name.
Overloading does not depend on the return value
Type Safe linkage
There is an added benefit to all of this name decoration. A particularly sticky problem in c occurs when the client programmer misdeclares function or worse a function is called without declaring it first and the compiler infers the function declaration from the way it is called. Sometimes this function declaration is correct but when it is not it can be a difficult bug to find.
Because all functions must be declared before they used in c++ . the opportunity for this problem are very rarely.
The c++ compiler refuses to declare a function automatically for you . so it is likely that you will include the appropriate header file.
However if for some reason you still manage a misdeclare a function either by declaring by hand or including the wrong header file the name decoration provides a safety net that is often referred to as type safe linkage.
Consider the following scenario. In one file is the definition for the function.
Void f(int){}
In the second file the function is not declare properly then called.
Void f(char);
Int main() {
// f(1)
}
Even though you can see that the function is actually f(int) the compiler does not know because it was told – through the explicit declaration that the function is f(char). Thus the compilation is successful.
In c the linker would also be successful but not in c++ Because the complier decorates the names the definition becomes something like f_int whereas the use of the function is f_char.
When the linker tries to resolve the reference to f_char it can only find f_int and give you error message.
Overloading Example
We can now modify earlier example to use function overloading.
As stated before an immediately useful place for overloading is in constructor.
You can see this in the following version of the stash.
Class Stash
{
Int size;
Int quantity;
Int next;
Unsigned char* storage;
Void inflate(int increase);
Public:
Stash(int size);
Stash(int size , int initQuantity);
~Stash();
Int add(void *element);
Void *fetch(int index);
Int count();
};
The first stash() constructor is the same as before but the second one has a Quantity argument to indicate the initial number of the storage places to b Allocated.
In the definition, you can see that the internal value of quantity is set to zero.Along with the storage pointer.
Overloading does not depend on the return value.
Unions
As you have seen the only difference between struct and class in C++ is that Struct default to public and class default to private.
Union can also have a constructor, destructor ,member function and even access control.
#include<iostream>
Using namespace std;
Union U {
Private:
Int I;
Float f;
Public :
U(int a);
U(float b);
~U();
Int read_int();
Float read_float();
};
U::U(int a)
{
I=a;
}
U::U(float b) { f =b; }
U::~U()
{
Cout<<” U::~U()\n”; }
Int U:: read_int()
{ return I;
}
Float U :: read_float()
{
Return f;
}
Int main()
{
U X(12),Y(1.9F);
}
You might think from the code above that the only difference between a union and class is the way the data is stored. ( int and float are overlaid on the same piece of storage).
Union can not be used as a base class during inheritance.
Although the member functions civilize access to the union somewhat there is still no way to prevent the client programmer from the selecting the wrong element type once the union is initialized.
In the example above you could say X.read_float() even though it is inappropriate.( you are float function to integer object)
However a “safe” union can be encapsulated in a class.
#include<iostream>
Using namespace std;
Class SuperVar {
Enum {
Character,
Integer,
Floating_point
}varitype;
Enum has no typename ( it is untagged enumeration) .This is acceptable if you are going to immediately define instances of the enum
Union {
Char c;
Int I;
Float f;
};
The union has no type name and no variable name. This is called an anonymous union and create space for union but does not require accessing the union element with a variable name and dot operator.
Public:
SuperVar(char ch);
SuperVar(int ii);
SuperVar(float ff);
Void print();
};
SuperVar::SuperVar(char ch)
{
Vartype =character;
C=ch;
}
SuperVar::SuperVar(int ii)
{
Vartype =integer;
I=ii;
}
SuperVar::SuperVar(float ff)
{
Vartype =floating_point;
F= FF;
}
Void SuperVar::print() {
Switch(vartype) {
Case character :
Cout<<” character “<<c<<endl;
Break;
Case integer :
Cout<<”integer “<<i<<endl;
Break;
Case floating_point:
Cout<<”float”<<f<<endl;
Break;
}
}
Int main()
{
SuperVar A(‘c’), B(12),c(1.4F);
}
The first purpose of the union in the first place to save union.
Default argument:
C++ provides a remedy default argument. A default argument is a value given in the declaration that the compiler automatically inserts if you do not provide a value in the function call.
Stash(int size);
Stash(int size, int quantity =0);
Default arguments are a convenience as function overloading is a convenience.
Both feature allow you to use a single fuction name in different situation.
The difference is that with default arguments the complier is substituting argument when you do not want to put them in yourself.
If the function have very different behaviors it does not make sense to use default argument.
There are two rules you must be aware of when using default argument.
First only trailing argument may be defaulted .That ‘s you can not have
A default argument followed by the non default argument.
Once you start using default argument in a particular function call , the subsequent argument in that function’s argument list must be defaulted.
Function overloading simple facts
http://codepad.org/LfOfvwOj
http://codepad.org/2ETEJcEG#comment-Yk8miiVk
http://codepad.org/tWoRl7dN
http://msdn.microsoft.com/en-us/library/kbd4xa4d.aspx
Placeholder Argument
Argument in a function declaration can be declared without identifiers.Const in header file
A const in C++ defaults to internal linkage that is it is visible only within the file where it is defined and can not be seen at link time by other translation unit. You must always assign a value to const where it is defined and can not seen at the link by the other translation unit. You must always assign a value to a const when
You define it. Expect when you make an explicit declaration using extern.
Safety Consts
The use of const is not limited to replacing #define in constant expression. If you initialize a variable with a value that is produced at the runtime and you will not change for the lifetime of that variable.
#include<iostream>
Using namespace std;
Const int i=100;
Const int j= i+10;
Long address =(long)&j;
Char buf[j+10];
Int main()
{
Cout<<” type a character”<<endl;
Const char c= cin.get() //can not chage
Const char c2 =c+’a’;
Const<<c2;
}
Difference with C++
The meaning of const in C “an ordinary variable that can not be changed”
In C a const always occupies storage and its name is global. The C compiler can not treat const as compile time constant.
In C if you say
Const
int bufsize =100;
Char buf[bufsize];
You will get an error even though it seems like a rational things to do. Because bufsize occupies storage somewhere. The C compiler can not know the value at compile time. You can optionally say
Const int bufsize;
C defaults to external linkage for consts this make sense. C++ defaults to internal linkage for const . So if you want to accomplish the same things in c++
You must explicitly change the linkage to external using extern.
Extern const int bufsize;
This line also works in C.
In C++, a const does not necessarily create storage. In C a const always create
Storage.
In C++ a const that is outside all function has file scope (i.e. it is invisible outside
The file. That is, it is default to internal linkage.
This is very different from all other identifiers in c++ that default to external linkage. Thus if you declare a const of the same name in two different file
And you do not take the address or define that name as extern. The ideal c++
Compiler will not allocate storage for the const but simply fold it into the code.
PointerWhen using const with pointer you have two options
Const can be applied to what the pointer is pointing to.
Const can be applied to the address stored in the pointer itself.
Pointer to Const
So if you want to prevent any changes to the element you are pointing to you write a definition like this.
Const int *u;
U is a pointer which points to a const int
Int const *V
V is const pointer to an int
Both the effect are same.
Const pointer
Int d =1;
Int * const w= &d;
W is pointer which is const that point to int.
Assignment and type checking
C++ is very particular about type checking and this extends to pointer assignment. You can assign the address of a non const object to a const
Pointer because you are simply promising not to change something that is
Ok to change.
You can not assign the address of a const object to a non const pointer.
Character array literals
The place where strict constness is not enforced is with character array literals.
Char *cp =”howdy”
The compiler will accept it without complaint. This is technically an error because
Because a character array literal is created by the compiler as a constant character array and the result of the quoted character array is its starting address
In memory.
If you want to be able to modify the string put it in an array
Char cp[] =”howbdy”
Function arguments & return values
Passing by const value
You can specify that function arguments are const when passing them by value such as
Void f1(const int i) {
I++; compile time error
}
The const takes on meaning the argument can not be changed .so it is really tool
For the creator and not the caller.
Returning by the const value
A similar truth hold for the return value. If you say that function’s return value is const.
Const int g().
You are promising that original variable will not be modified inside the function.
Int f3() { return 1;}
Const int f4() { return 1; }
Int main()
{
Const int j =f3(); //works fine
Int k =f4(); // But this works fine too
}
For built in types it does not matter whether you return by value as const so you avoid confusing the client programmer and leave off the const when returning a built in type by value.
Returning by value as a const becomes important when you are dealing with user defined types.
Class X {
Int I;
Public:
X(int ii =0);
Void modify();
};
X::X(int ii)
{
I =ii;
}
Void X::modify()
{
I++;
}
X f5() - No constant
{
Return X();
}
Const X f6() { - Return Constant
Return X();
}
Void f7(X& x) Pass by Const
{
x.modify();
}
}
Int main()
{
F5() = X(1); //ok non const return value
F5() return a non const X object while f6() return a const X object. Only the non const return value can be used as an lvalue.
Passing and returning addresses
Page No 349 (Thinking in c++)
If you pass or return an address( either a pointer or reference) it’s possible for the client programmer to take it and modify the original value.
If you make the pointer or reference a const you prevent this from happening.
In fact when you are passing an address into a function, you should make it a const if at all possible.
The choice of whether to return a pointer or reference to a const depends on what you want to allow your client programmer to do with it.
Void t(int *) { } - ordinary pointer
Void u(const int* cip)
{
*cip =2 // illegal Modifing value.
Int I = *cip // ok copies value
Int *p2 =cip // illegal non const value
}
Const char *v() {
// Return address of static character array
Return “result of function v()”
}
Const int * const w () {
Static int I;
Return &I;
}
Int main()
{
Int x =0;
Int *ip =&x;
Const int *cip =&x;
T(ip); //ok
// t(cip) //Not ok you can not assign const pointer to non const pointer
U(ip) //ok
U(cip) // ok
Char *cp =v() // not ok
Const char *ccp =v(); //ok
Int *ip2 =w(); //Not ok
Const int* const ccip =w(); //ok
Const int* cip2 =w() //ok
*w() =1 // Not ok
}
Standard argument Passing
In C it is very common to pass by value and when you want to pass an address your only choice is to use pointer.
In C++ your first choice pass by reference and by const reference at that.
Because of the syntax reference, it is possible to pass a temporary object to a function that takes a const reference. In the other case you can never pass a temporary object to function that takes a pointer.
With a pointer, the address must be explicitly taken.
The example will better explain.
ConstTemporary.cpp
Class X {};
X f() { return X(); }
Void g1(X&) {} // Pass by the non const reference.
Void g2(const X&) { }
Int main()
{
// g1(f())
// Error: const temporary created by the ()
// g2(f())
}
F() returns an objects of class X by value. That means when you immediately take the return value of f() and pass it to another function as in the calls to g1() and g2(), a temporary is created and that temporary is const.
The call in g1() is an error because g1() does not take a const reference but the call to g2() is ok
Classes
This section shows the way you can use const with classes. You may want to create a local const in a class to use inside constant expression that will be
Evaluated at the compile time.
Const in classes
One of the places you did like to use a const for constant expression is inside classes. The typical example is when you are creating an array inside a class
And you want to use a const instead of a #define to establish the array size
And to use in calculation involving the array. The array size is something you did like to keep hidden inside the class. So if you used a name like size for example
You could use that name in another class without a clash.
The preprocessor treats all #define as global from the point they are defined, so
This will not achieve the desire effect.
The constructor initialize list
The special initialization point is called the constructor initialize list and it was originally developed for use in inheritance.
The constructor initializer list – which as the name implies, occur only in the definition of the constructor.
The proper form for const inside a class is shown here.
#include<iostream>
Using namespace std;
Class Fred
{
Const int size;
Public:
Fred(int sz);
Void print()
};
Fred :: Fred(int sz) : size(sz) {}
Void Fred :: print() { cout <<”size “<<endl}
Int main()
{
Fred a(1),b(2),c(3);
a.print(),b.print(),c.print();
}
“Constructor” for built in type
You can treat a built in type as if it has a constructor like this.
#include<iostream>
Using namespace std;
Class B
{
Int I;
Public:
B(int ii);
Void print()
};
B::B(int ii) : i(ii) { }
Void B::print() {
Cout<<I <<endl;
}
Int main()
{
B a(1),b(2);
}
Compile time constants in classes
The above use of const is interesting and probably useful in cases but it does not solve the original problem.
The static keyword in this situation means “there is only one instances regardless of how many objects of the classes are created”
There is one feature of static const when used inside the classes which is bit unusal, you must provide the initialize at the point of definition of the static const
#include<string>
#include<iostream>
Using namespace std;
Class StringStack
{
Static const int size =100;
Const string* stack[size];
int index;
}
The “enum hack” in old code
In older version of c++, static const was not supported inside classes. This meant
The const was useless for constant expression inside the classes.
#include<iostream>
Using namespace std;
Class Bunch {
Enum { size =100};
Int i[size];
};
Int main()
{
Cout<<” sizeof (bunch) “<<sizeof(bunch)<<endl;
}
The use of enum here is guaranteed to occupy no storage in the object.
Enum { one =1, two =2, three };
The compiler will continue counting from the last value so the enumerator three will get the value 3
http://codepad.org/FFQR9O5A
Cosnt objects & member funciton
Class member function can be made const.
A const object is defined the same for a user defined type as a built in type.
Const int I =1;
Page no 359
Static variable inside the function
The storage for this object is not on the stack but instead in the program’s static
data area. This object is initialized only once the first time the function is called
and retain its value between function invocation
if you do not provide an initialize for a static variable of a built in type, the compiler guarantees that variable will be initialized to zero.
Static class objects inside function.The rules are the same for static objects of user-defined types, including the fact that some initialization is required for the object.
User defined types must be initialized with the constructor calls.
Using namespace std;
Class X {
Int I;
Public:
X(int ii =0) :i(ii) {} //default
~X() { cout<<” X:: ~X()”<<endl; }
};
Void f() {
Static X x1(47);
Static X x2; // default constructor required
}
Int main() {
F()
}
Deep copy Program
http://codepad.org/IFjgA6fd
Static object destructors
Destructor for static objects are called when main() exits or when the standard C library function exit() is explicitly called.
Controlling linkage
Any name at file scope is visible throughout all translation unit in a program This is often called external linkage Static is local to its translation unit. That names has internal linkage .This means
That you can use the same name in other translation unit without a name clash
One advantage to internal linkage is that the name can be placed in a header file
Without worrying that there will be clash at link time
Static VS global
If I declare in the file following function. It will represent two things
Int a=0;
The storage for a will be in the program’s static data area and the initialization
For a will occur once before main() entered. In the addition the visibility of a is global across all translation unit.
If it is
Static int a =0;
The variable a has internal linkage the storage class unchanged but scope is limited to file lavel
If it is
Using namespace std;
Int main()
{
Extern int I;
Std::cout << I;
}
In another file
Int I =5;
It is same like global function
Others storage class specifiers
Because C++ does not know much about your class, the default copy constructor and default assignment operators it provides use a copying method known as a shallow copy (also known as a memberwise copy).
A shallow copy means that C++ copies each member of the class individually using the assignment operator. When classes are simple (eg. do not contain any dynamically allocated memory), this works very well.
For example, let’s take a look at our Cents class
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents=0)
{
m_nCents = nCents;
}
};
When C++ does a shallow copy of this class, it will copy m_nCents using the standard integer assignment operator. Since this is exactly what we’d be doing anyway if we wrote our own copy constructor or overloaded assignment operator, there’s really no reason to write our own version of these functions!
However, when designing classes that handle dynamically allocated memory, memberwise (shallow) copying can get us in a lot of trouble! This is because the standard pointer assignment operator just copies the address of the pointer — it does not allocate any memory or copy the contents being pointed to!
Let’s take a look at an example of this:
class MyString
{
private:
char *m_pchString;
int m_nLength;
public:
MyString(char *pchString="")
{
// Find the length of the string
// Plus one character for a terminator
m_nLength = strlen(pchString) + 1;
// Allocate a buffer equal to this length
m_pchString= new char[m_nLength];
// Copy the parameter into our internal buffer
strncpy(m_pchString, pchString, m_nLength);
// Make sure the string is terminated
m_pchString[m_nLength-1] = '\\0';
}
~MyString() // destructor
{
// We need to deallocate our buffer
delete[] m_pchString;
// Set m_pchString to null just in case
m_pchString = 0;
}
char* GetString() { return m_pchString; }
int GetLength() { return m_nLength; }
};
The above is a simple string class that allocates memory to hold a string that we pass in. Note that we have not defined a copy constructor or overloaded assignment operator. Consequently, C++ will provide a default copy constructor and default assignment operator that do a shallow copy.
Now, consider the following snippet of code:
MyString cHello("Hello, world!");
{
MyString cCopy = cHello; // use default copy constructor
} // cCopy goes out of scope here
std::cout << cHello.GetString() << std::endl; // this will crash
While this code looks harmless enough, it contains an insidious problem that will cause the program to crash! Can you spot it? Don’t worry if you can’t, it’s rather subtle.
Let’s break down this example line by line:
MyString cHello("Hello, world!");
This line is harmless enough. This calls the MyString constructor, which allocates some memory, sets cHello.m_pchString to point to it, and then copies the string “Hello, world!” into it
MyString cCopy = cHello; // use default copy constructor
This line seems harmless enough as well, but it’s actually the source of our problem! When this line is evaluated, C++ will use the default copy constructor (because we haven’t provided our own), which does a shallow pointer copy on cHello.m_pchString. Because a shallow pointer copy just copies the address of the pointer, the address of cHello.m_pchString is copied into cCopy.m_pchString. As a result, cCopy.m_pchString and cHello.m_pchString are now both pointing to the same piece of memory!
} // cCopy goes out of scope here
When cCopy goes out of scope, the MyString destructor is called on cCopy. The destructor deletes the dynamically allocated memory that both cCopy.m_pchString and cHello.m_pchString are pointing to! Consequently, by deleting cCopy, we’ve also (inadvertently) affected cHello. Note that the destructor will set cCopy.m_pchString to 0, but cHello.m_pchString will be left pointing to the deleted (invalid) memory!
std::cout << cHello.GetString() << std::endl;
Now you can see why this crashes. We deleted the string that cHello was pointing to, and now we are trying to print the value of memory that is no longer allocated.
The root of this problem is the shallow copy done by the copy constructor — doing a shallow copy on pointer values in a copy constructor or overloaded assignment operator is almost always asking for trouble.
Deep copying
The answer to this problem is to do a deep copy on any non-null pointers being copied. A deep copy duplicates the object or variable being pointed to so that the destination (the object being assigned to) receives it’s own local copy. This way, the destination can do whatever it wants to it’s local copy and the object that was copied from will not be affected. Doing deep copies requires that we write our own copy constructors and overloaded assignment operators.
Let’s go ahead and show how this is done for our MyString class:
// Copy constructor
MyString::MyString(const MyString& cSource)
{
// because m_nLength is not a pointer, we can shallow copy it
m_nLength = cSource.m_nLength;
// m_pchString is a pointer, so we need to deep copy it if it is non-null
if (cSource.m_pchString)
{
// allocate memory for our copy
m_pchString = new char[m_nLength];
// Copy the string into our newly allocated memory
strncpy(m_pchString, cSource.m_pchString, m_nLength);
}
else
m_pchString = 0;
}
As you can see, this is quite a bit more involved than a simple shallow copy! First, we have to check to make sure cSource even has a string (line 8). If it does, then we allocate enough memory to hold a copy of that string (line 11). Finally, we have to manually copy the string using strncpy() (line 14).
Now let’s do the overloaded assignment operator. The overloaded assignment operator is a tad bit trickier:
// Assignment operator
MyString& MyString::operator=(const MyString& cSource)
{
// check for self-assignment
if (this == &cSource)
return *this;
// first we need to deallocate any value that this string is holding!
delete[] m_pchString;
// because m_nLength is not a pointer, we can shallow copy it
m_nLength = cSource.m_nLength;
// now we need to deep copy m_pchString
if (cSource.m_pchString)
{
// allocate memory for our copy
m_pchString = new char[m_nLength];
// Copy the parameter the newly allocated memory
strncpy(m_pchString, cSource.m_pchString, m_nLength);
}
else
m_pchString = 0;
return *this;
}
Note that our assignment operator is very similar to our copy constructor, but there are three major differences:
We added a self-assignment check (line 5).
We return *this so we can chain the assignment operator (line 26).
We need to explicitly deallocate any value that the string is already holding (line 9).
When the overloaded assignment operator is called, the item being assigned to may already contain a previous value, which we need to make sure we clean up before we assign memory for new values. For non-dynamically allocated variables (which are a fixed size), we don’t have to bother because the new value just overwrite the old one. However, for dynamically allocated variables, we need to explicitly deallocate any old memory before we allocate any new memory. If we don’t, the code will not crash, but we will have a memory leak that will eat away our free memory every time we do an assignment!
Deep copy in my words
I wrote simple and get crash and did little bit analysis on this issue.
#include<iostream>
using namespace std;
class sample
{
char *c_string;
public:
sample()
{
cout<<" the default constructor is "<<endl;
}
sample(char *p)
{
int i=0;
while(p[i]!='\0')
i++;
c_string =new char[i+1];
i=0;
while(p[i]!='\0')
{
c_string[i]=p[i];
i++;
}
char* getstring()
{
return c_string;
}
~sample()
{
delete[] c_string;
}
};
int main()
{
sample c("hello");
{
sample copy =c; // want to write this line so i have put fix and find the program below
}
cout<<"The value of c"<<c.getstring()<<endl<<endl;
return 0;
}
So I saw program crash i hope you have got understanding from the above article.
The value of c
*** glibc detected *** ./sh: double free or corruption (fasttop): 0x00000000154f1010 ***
======= Backtrace: =========
/lib64/libc.so.6[0x2add27eb82ef]
/lib64/libc.so.6(cfree+0x4b)[0x2add27eb873b]
./sh(__gxx_personality_v0+0x2dc)[0x400ac4]
./sh(__gxx_personality_v0+0x1e4)[0x4009cc]
/lib64/libc.so.6(__libc_start_main+0xf4)[0x2add27e63994]
./sh(__gxx_personality_v0+0x51)[0x400839]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:1a 1365237173 /x/home-new/agoswami/my_work/sh
00601000-00602000 rw-p 00001000 00:1a 1365237173 /x/home-new/agoswami/my_work/sh
154f1000-15512000 rw-p 154f1000 00:00 0 [heap]
2add27497000-2add274b3000 r-xp 00000000 fd:00 786434 /lib64/ld-2.5.so
2add274b3000-2add274b5000 rw-p 2add274b3000 00:00 0
2add274c8000-2add274c9000 rw-p 2add274c8000 00:00 0
2add276b2000-2add276b3000 r--p 0001b000 fd:00 786434 /lib64/ld-2.5.so
2add276b3000-2add276b4000 rw-p 0001c000 fd:00 786434 /lib64/ld-2.5.so
2add276b4000-2add2779a000 r-xp 00000000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add2779a000-2add27999000 ---p 000e6000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add27999000-2add2799f000 r--p 000e5000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add2799f000-2add279a2000 rw-p 000eb000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add279a2000-2add279b4000 rw-p 2add279a2000 00:00 0
2add279b4000-2add27a36000 r-xp 00000000 fd:00 786449 /lib64/libm-2.5.so
2add27a36000-2add27c35000 ---p 00082000 fd:00 786449 /lib64/libm-2.5.so
2add27c35000-2add27c36000 r--p 00081000 fd:00 786449 /lib64/libm-2.5.so
2add27c36000-2add27c37000 rw-p 00082000 fd:00 786449 /lib64/libm-2.5.so
2add27c37000-2add27c44000 r-xp 00000000 fd:00 786486 /lib64/libgcc_s-4.1.2-20080825.so.1
2add27c44000-2add27e44000 ---p 0000d000 fd:00 786486 /lib64/libgcc_s-4.1.2-20080825.so.1
2add27e44000-2add27e45000 rw-p 0000d000 fd:00 786486 /lib64/libgcc_s-4.1.2-20080825.so.1
2add27e45000-2add27e46000 rw-p 2add27e45000 00:00 0
2add27e46000-2add27f93000 r-xp 00000000 fd:00 786441 /lib64/libc-2.5.so
2add27f93000-2add28193000 ---p 0014d000 fd:00 786441 /lib64/libc-2.5.so
2add28193000-2add28197000 r--p 0014d000 fd:00 786441 /lib64/libc-2.5.so
2add28197000-2add28198000 rw-p 00151000 fd:00 786441 /lib64/libc-2.5.so
2add28198000-2add2819e000 rw-p 2add28198000 00:00 0
7fffac843000-7fffac858000 rw-p 7ffffffea000 00:00 0 [stack]
ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0 [vdso]
Aborted
sample(const sample& s)
{
int i=0;
while(s.c_string[i]='\0')
i++;
c_string=new char[i+1];
i=0;
while(s.c_string[i]!='\0')
{
c_string[i]=s.c_string[i];
i++;
}
c_string[i]='\0';
}
so i addes this function and able to write this line
but In main I want to write the below line.
sample d;
d=c;
than i have to write the following function.
char* operator=(const sample &s)
{
int i=0;
while(s.c_string[i]!='\0')
i++;
c_string =new char[i+1];
i=0;
while(s.c_string[i]!='\0')
{
c_string[i] =s.c_string[i];
i++;
}
c_string[i]='\0';
return c_string;
}
Class X
{
int i;
public:
X();
}
void f() is
{
X a;
}
the same thing happen as if a were an int. Storage is allocated for object But when the program reaches the sequence point(point of execution) where a is defined, the constructor is called automatically. That is, the compiler quietly insert the
call to X::X() for the object a at point of definition. like any member function the first secret argument to the constructor is the this pointer the address of the object for which it is being called .in the case of constructor however this is pointing to an unintialized block of memory and it is job of the constructor to intialize this memory properly.
If u have constructor with the argument than you can initialize object with argument. Tree a(2).
Constructor and destructor can not have return value.
In the Destructor never has any argument because destruction never need any option.
Class Y {
Public:
~Y();
};
Example of constructor and destructors
#include<iostream>
Using namespace std;
Class Tree
{
Int height;
Public :
Tree( initialHeight);
~Tree();
Void grow(int years);
Void printsize();
Void grow(int years);
Void printsize();
};
like any function, the constructor can have argument to allow you to specify how an object is created.
A default constructor is one that can be called with no argument.
Storage allocation:
A variable can now be defined at any point in a scope. So it might seem that the storage for the variable may not be defined until its point of definition.
The storage is allocated at the beginning begining of the block, the constructor
Call does not happen until the sequence point where the object is defined because the identifier is not available until then.
Aggregate initialization
An aggregate is just what it sounds like : a bunch of things clumped together.
This definition includes aggregates of mixed types like struct and classes. An array is an aggregate of a single type.
When you create an object that’s an aggregate all you must do is make assignment and the initialization will be taken care of by the compiler.
This assignment can be in several flavors For an array of built in type this is quite simple
Int a[5] ={1,2,3,4,5}
If u try to give more initializers than there are array element , the compiler gives an error message.
Int b[6] ={0}
A second shorthand for arrays is automatic counting in which you let the compiler
Determine the size of the array based on the number of initializers.
Int c[] ={ 1,2,3,4 }
Now if you decide to add another element to the array you simply add another initialize.
The size of array will be sizeof c /sizeof *c ( size of the entire array deivided by the size of the first element)
For(int I =0; i< sizeof c/ sizeof *c ;i++)
C[i]++;
Structure are also aggregates , they can be initialized in a similar fashion. Because a C style struct has all of its members.
Struct X
{
Int I;
Float f;
Char c;
};
X x1 ={1,2.2.’c’}
If you have an array of such object you can initialize them by using a nested set of curly braces for each object.
X x2[3] ={ {1,1.1,’a’} , {2,2.2.,’b’}}
Here the third object is initialized to zero.
If you have struct with all members public or a class with private data member all the initialization must go through the constructor even if you are using aggregat Initialization.
Second example showing multiple constructor argument.
#include<iostream>
Using namespace std;
Class z {
Int I,j;
Public:
Z(int ii ,int jj);
Void print ();
};
Z::Z(int ii , int jj)
{
I =ii;
J=jj;
}
Void Z::print() {
Cout<<” I =”<<I <<” j=”<<j<<endl;
}
Int main()
{
Z zz[] = { Z(1,2) ,Z(3,4) , Z(5,6) ,Z(7,8) };
For(int I =0 ; i< sizeof zz / sizeof *zz ; i++)
Zz[i].print();
Notice that it look like an explicit constructor is called for each object in the array.
}
Default constructor:
A default constructor is one that can be called with no argument. A default constructor is used to create a “vanilla object” but it is also important when the
Compiler is told to create an object but is not given details.
For Example if you take the struct Y defined previously and use it in a definition like this .
Y y2[2] ={ Y(1) };
The complier will complain that it can not find a default constructor.
The second object in the array wants to be created with no argument
And that’s where the compiler looks for a default constructor.
In fact if you simply define an array of objects
Y y3[7];
The complier will complain because it must have a default constructor to initialize every object in the array.
The default constructor is so important if and only if there are no constructor for a constructor
Function overloading and default argument
More name decoration
The concept of name decoration was introduced.
Void f()
Class X
{
Void f();
}
The function f() inside the scope of class X does not clash with the global version of f().
The compiler perform this scoping by the manufacturing different internal name for the global version of f()and X::f().
It was suggested that the name are simply the class name “decorated” together
Function name so the internal names the compiler uses might be _f and _X_f.
The compiler will give the name with the argument list with function name. it does not generate compiler name only with the function name.
Overloading does not depend on the return value
Type Safe linkage
There is an added benefit to all of this name decoration. A particularly sticky problem in c occurs when the client programmer misdeclares function or worse a function is called without declaring it first and the compiler infers the function declaration from the way it is called. Sometimes this function declaration is correct but when it is not it can be a difficult bug to find.
Because all functions must be declared before they used in c++ . the opportunity for this problem are very rarely.
The c++ compiler refuses to declare a function automatically for you . so it is likely that you will include the appropriate header file.
However if for some reason you still manage a misdeclare a function either by declaring by hand or including the wrong header file the name decoration provides a safety net that is often referred to as type safe linkage.
Consider the following scenario. In one file is the definition for the function.
Void f(int){}
In the second file the function is not declare properly then called.
Void f(char);
Int main() {
// f(1)
}
Even though you can see that the function is actually f(int) the compiler does not know because it was told – through the explicit declaration that the function is f(char). Thus the compilation is successful.
In c the linker would also be successful but not in c++ Because the complier decorates the names the definition becomes something like f_int whereas the use of the function is f_char.
When the linker tries to resolve the reference to f_char it can only find f_int and give you error message.
Overloading Example
We can now modify earlier example to use function overloading.
As stated before an immediately useful place for overloading is in constructor.
You can see this in the following version of the stash.
Class Stash
{
Int size;
Int quantity;
Int next;
Unsigned char* storage;
Void inflate(int increase);
Public:
Stash(int size);
Stash(int size , int initQuantity);
~Stash();
Int add(void *element);
Void *fetch(int index);
Int count();
};
The first stash() constructor is the same as before but the second one has a Quantity argument to indicate the initial number of the storage places to b Allocated.
In the definition, you can see that the internal value of quantity is set to zero.Along with the storage pointer.
Overloading does not depend on the return value.
Unions
As you have seen the only difference between struct and class in C++ is that Struct default to public and class default to private.
Union can also have a constructor, destructor ,member function and even access control.
#include<iostream>
Using namespace std;
Union U {
Private:
Int I;
Float f;
Public :
U(int a);
U(float b);
~U();
Int read_int();
Float read_float();
};
U::U(int a)
{
I=a;
}
U::U(float b) { f =b; }
U::~U()
{
Cout<<” U::~U()\n”; }
Int U:: read_int()
{ return I;
}
Float U :: read_float()
{
Return f;
}
Int main()
{
U X(12),Y(1.9F);
}
You might think from the code above that the only difference between a union and class is the way the data is stored. ( int and float are overlaid on the same piece of storage).
Union can not be used as a base class during inheritance.
Although the member functions civilize access to the union somewhat there is still no way to prevent the client programmer from the selecting the wrong element type once the union is initialized.
In the example above you could say X.read_float() even though it is inappropriate.( you are float function to integer object)
However a “safe” union can be encapsulated in a class.
#include<iostream>
Using namespace std;
Class SuperVar {
Enum {
Character,
Integer,
Floating_point
}varitype;
Enum has no typename ( it is untagged enumeration) .This is acceptable if you are going to immediately define instances of the enum
Union {
Char c;
Int I;
Float f;
};
The union has no type name and no variable name. This is called an anonymous union and create space for union but does not require accessing the union element with a variable name and dot operator.
Public:
SuperVar(char ch);
SuperVar(int ii);
SuperVar(float ff);
Void print();
};
SuperVar::SuperVar(char ch)
{
Vartype =character;
C=ch;
}
SuperVar::SuperVar(int ii)
{
Vartype =integer;
I=ii;
}
SuperVar::SuperVar(float ff)
{
Vartype =floating_point;
F= FF;
}
Void SuperVar::print() {
Switch(vartype) {
Case character :
Cout<<” character “<<c<<endl;
Break;
Case integer :
Cout<<”integer “<<i<<endl;
Break;
Case floating_point:
Cout<<”float”<<f<<endl;
Break;
}
}
Int main()
{
SuperVar A(‘c’), B(12),c(1.4F);
}
The first purpose of the union in the first place to save union.
Default argument:
C++ provides a remedy default argument. A default argument is a value given in the declaration that the compiler automatically inserts if you do not provide a value in the function call.
Stash(int size);
Stash(int size, int quantity =0);
Default arguments are a convenience as function overloading is a convenience.
Both feature allow you to use a single fuction name in different situation.
The difference is that with default arguments the complier is substituting argument when you do not want to put them in yourself.
If the function have very different behaviors it does not make sense to use default argument.
There are two rules you must be aware of when using default argument.
First only trailing argument may be defaulted .That ‘s you can not have
A default argument followed by the non default argument.
Once you start using default argument in a particular function call , the subsequent argument in that function’s argument list must be defaulted.
Function overloading simple facts
http://codepad.org/LfOfvwOj
http://codepad.org/2ETEJcEG#comment-Yk8miiVk
http://codepad.org/tWoRl7dN
http://msdn.microsoft.com/en-us/library/kbd4xa4d.aspx
Placeholder Argument
Argument in a function declaration can be declared without identifiers.Const in header file
A const in C++ defaults to internal linkage that is it is visible only within the file where it is defined and can not be seen at link time by other translation unit. You must always assign a value to const where it is defined and can not seen at the link by the other translation unit. You must always assign a value to a const when
You define it. Expect when you make an explicit declaration using extern.
- Extern const int bufsize :Normally the C++ compiler avoid creating storage for a const but instead hold the Definitions in its symbol table. When we extern keyword it forces us to allocate storage.
Safety Consts
The use of const is not limited to replacing #define in constant expression. If you initialize a variable with a value that is produced at the runtime and you will not change for the lifetime of that variable.
#include<iostream>
Using namespace std;
Const int i=100;
Const int j= i+10;
Long address =(long)&j;
Char buf[j+10];
Int main()
{
Cout<<” type a character”<<endl;
Const char c= cin.get() //can not chage
Const char c2 =c+’a’;
Const<<c2;
}
Difference with C++
The meaning of const in C “an ordinary variable that can not be changed”
In C a const always occupies storage and its name is global. The C compiler can not treat const as compile time constant.
In C if you say
Const
int bufsize =100;
Char buf[bufsize];
You will get an error even though it seems like a rational things to do. Because bufsize occupies storage somewhere. The C compiler can not know the value at compile time. You can optionally say
Const int bufsize;
C defaults to external linkage for consts this make sense. C++ defaults to internal linkage for const . So if you want to accomplish the same things in c++
You must explicitly change the linkage to external using extern.
Extern const int bufsize;
This line also works in C.
In C++, a const does not necessarily create storage. In C a const always create
Storage.
In C++ a const that is outside all function has file scope (i.e. it is invisible outside
The file. That is, it is default to internal linkage.
This is very different from all other identifiers in c++ that default to external linkage. Thus if you declare a const of the same name in two different file
And you do not take the address or define that name as extern. The ideal c++
Compiler will not allocate storage for the const but simply fold it into the code.
PointerWhen using const with pointer you have two options
Const can be applied to what the pointer is pointing to.
Const can be applied to the address stored in the pointer itself.
Pointer to Const
So if you want to prevent any changes to the element you are pointing to you write a definition like this.
Const int *u;
U is a pointer which points to a const int
Int const *V
V is const pointer to an int
Both the effect are same.
Const pointer
Int d =1;
Int * const w= &d;
W is pointer which is const that point to int.
Assignment and type checking
C++ is very particular about type checking and this extends to pointer assignment. You can assign the address of a non const object to a const
Pointer because you are simply promising not to change something that is
Ok to change.
You can not assign the address of a const object to a non const pointer.
Character array literals
The place where strict constness is not enforced is with character array literals.
Char *cp =”howdy”
The compiler will accept it without complaint. This is technically an error because
Because a character array literal is created by the compiler as a constant character array and the result of the quoted character array is its starting address
In memory.
If you want to be able to modify the string put it in an array
Char cp[] =”howbdy”
Function arguments & return values
Passing by const value
You can specify that function arguments are const when passing them by value such as
Void f1(const int i) {
I++; compile time error
}
The const takes on meaning the argument can not be changed .so it is really tool
For the creator and not the caller.
Returning by the const value
A similar truth hold for the return value. If you say that function’s return value is const.
Const int g().
You are promising that original variable will not be modified inside the function.
Int f3() { return 1;}
Const int f4() { return 1; }
Int main()
{
Const int j =f3(); //works fine
Int k =f4(); // But this works fine too
}
For built in types it does not matter whether you return by value as const so you avoid confusing the client programmer and leave off the const when returning a built in type by value.
Returning by value as a const becomes important when you are dealing with user defined types.
Class X {
Int I;
Public:
X(int ii =0);
Void modify();
};
X::X(int ii)
{
I =ii;
}
Void X::modify()
{
I++;
}
X f5() - No constant
{
Return X();
}
Const X f6() { - Return Constant
Return X();
}
Void f7(X& x) Pass by Const
{
x.modify();
}
}
Int main()
{
F5() = X(1); //ok non const return value
F5() return a non const X object while f6() return a const X object. Only the non const return value can be used as an lvalue.
Passing and returning addresses
Page No 349 (Thinking in c++)
If you pass or return an address( either a pointer or reference) it’s possible for the client programmer to take it and modify the original value.
If you make the pointer or reference a const you prevent this from happening.
In fact when you are passing an address into a function, you should make it a const if at all possible.
The choice of whether to return a pointer or reference to a const depends on what you want to allow your client programmer to do with it.
Void t(int *) { } - ordinary pointer
Void u(const int* cip)
{
*cip =2 // illegal Modifing value.
Int I = *cip // ok copies value
Int *p2 =cip // illegal non const value
}
Const char *v() {
// Return address of static character array
Return “result of function v()”
}
Const int * const w () {
Static int I;
Return &I;
}
Int main()
{
Int x =0;
Int *ip =&x;
Const int *cip =&x;
T(ip); //ok
// t(cip) //Not ok you can not assign const pointer to non const pointer
U(ip) //ok
U(cip) // ok
Char *cp =v() // not ok
Const char *ccp =v(); //ok
Int *ip2 =w(); //Not ok
Const int* const ccip =w(); //ok
Const int* cip2 =w() //ok
*w() =1 // Not ok
}
Standard argument Passing
In C it is very common to pass by value and when you want to pass an address your only choice is to use pointer.
In C++ your first choice pass by reference and by const reference at that.
Because of the syntax reference, it is possible to pass a temporary object to a function that takes a const reference. In the other case you can never pass a temporary object to function that takes a pointer.
With a pointer, the address must be explicitly taken.
The example will better explain.
ConstTemporary.cpp
Class X {};
X f() { return X(); }
Void g1(X&) {} // Pass by the non const reference.
Void g2(const X&) { }
Int main()
{
// g1(f())
// Error: const temporary created by the ()
// g2(f())
}
F() returns an objects of class X by value. That means when you immediately take the return value of f() and pass it to another function as in the calls to g1() and g2(), a temporary is created and that temporary is const.
The call in g1() is an error because g1() does not take a const reference but the call to g2() is ok
Classes
This section shows the way you can use const with classes. You may want to create a local const in a class to use inside constant expression that will be
Evaluated at the compile time.
Const in classes
One of the places you did like to use a const for constant expression is inside classes. The typical example is when you are creating an array inside a class
And you want to use a const instead of a #define to establish the array size
And to use in calculation involving the array. The array size is something you did like to keep hidden inside the class. So if you used a name like size for example
You could use that name in another class without a clash.
The preprocessor treats all #define as global from the point they are defined, so
This will not achieve the desire effect.
The constructor initialize list
The special initialization point is called the constructor initialize list and it was originally developed for use in inheritance.
The constructor initializer list – which as the name implies, occur only in the definition of the constructor.
The proper form for const inside a class is shown here.
#include<iostream>
Using namespace std;
Class Fred
{
Const int size;
Public:
Fred(int sz);
Void print()
};
Fred :: Fred(int sz) : size(sz) {}
Void Fred :: print() { cout <<”size “<<endl}
Int main()
{
Fred a(1),b(2),c(3);
a.print(),b.print(),c.print();
}
“Constructor” for built in type
You can treat a built in type as if it has a constructor like this.
#include<iostream>
Using namespace std;
Class B
{
Int I;
Public:
B(int ii);
Void print()
};
B::B(int ii) : i(ii) { }
Void B::print() {
Cout<<I <<endl;
}
Int main()
{
B a(1),b(2);
}
Compile time constants in classes
The above use of const is interesting and probably useful in cases but it does not solve the original problem.
The static keyword in this situation means “there is only one instances regardless of how many objects of the classes are created”
There is one feature of static const when used inside the classes which is bit unusal, you must provide the initialize at the point of definition of the static const
#include<string>
#include<iostream>
Using namespace std;
Class StringStack
{
Static const int size =100;
Const string* stack[size];
int index;
}
The “enum hack” in old code
In older version of c++, static const was not supported inside classes. This meant
The const was useless for constant expression inside the classes.
#include<iostream>
Using namespace std;
Class Bunch {
Enum { size =100};
Int i[size];
};
Int main()
{
Cout<<” sizeof (bunch) “<<sizeof(bunch)<<endl;
}
The use of enum here is guaranteed to occupy no storage in the object.
Enum { one =1, two =2, three };
The compiler will continue counting from the last value so the enumerator three will get the value 3
http://codepad.org/FFQR9O5A
Cosnt objects & member funciton
Class member function can be made const.
A const object is defined the same for a user defined type as a built in type.
Const int I =1;
Page no 359
Static variable inside the function
The storage for this object is not on the stack but instead in the program’s static
data area. This object is initialized only once the first time the function is called
and retain its value between function invocation
if you do not provide an initialize for a static variable of a built in type, the compiler guarantees that variable will be initialized to zero.
Static class objects inside function.The rules are the same for static objects of user-defined types, including the fact that some initialization is required for the object.
User defined types must be initialized with the constructor calls.
Using namespace std;
Class X {
Int I;
Public:
X(int ii =0) :i(ii) {} //default
~X() { cout<<” X:: ~X()”<<endl; }
};
Void f() {
Static X x1(47);
Static X x2; // default constructor required
}
Int main() {
F()
}
Deep copy Program
http://codepad.org/IFjgA6fd
Static object destructors
Destructor for static objects are called when main() exits or when the standard C library function exit() is explicitly called.
Controlling linkage
Any name at file scope is visible throughout all translation unit in a program This is often called external linkage Static is local to its translation unit. That names has internal linkage .This means
That you can use the same name in other translation unit without a name clash
One advantage to internal linkage is that the name can be placed in a header file
Without worrying that there will be clash at link time
Static VS global
If I declare in the file following function. It will represent two things
Int a=0;
The storage for a will be in the program’s static data area and the initialization
For a will occur once before main() entered. In the addition the visibility of a is global across all translation unit.
If it is
Static int a =0;
The variable a has internal linkage the storage class unchanged but scope is limited to file lavel
If it is
Using namespace std;
Int main()
{
Extern int I;
Std::cout << I;
}
In another file
Int I =5;
It is same like global function
Others storage class specifiers
Because C++ does not know much about your class, the default copy constructor and default assignment operators it provides use a copying method known as a shallow copy (also known as a memberwise copy).
A shallow copy means that C++ copies each member of the class individually using the assignment operator. When classes are simple (eg. do not contain any dynamically allocated memory), this works very well.
For example, let’s take a look at our Cents class
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents=0)
{
m_nCents = nCents;
}
};
When C++ does a shallow copy of this class, it will copy m_nCents using the standard integer assignment operator. Since this is exactly what we’d be doing anyway if we wrote our own copy constructor or overloaded assignment operator, there’s really no reason to write our own version of these functions!
However, when designing classes that handle dynamically allocated memory, memberwise (shallow) copying can get us in a lot of trouble! This is because the standard pointer assignment operator just copies the address of the pointer — it does not allocate any memory or copy the contents being pointed to!
Let’s take a look at an example of this:
class MyString
{
private:
char *m_pchString;
int m_nLength;
public:
MyString(char *pchString="")
{
// Find the length of the string
// Plus one character for a terminator
m_nLength = strlen(pchString) + 1;
// Allocate a buffer equal to this length
m_pchString= new char[m_nLength];
// Copy the parameter into our internal buffer
strncpy(m_pchString, pchString, m_nLength);
// Make sure the string is terminated
m_pchString[m_nLength-1] = '\\0';
}
~MyString() // destructor
{
// We need to deallocate our buffer
delete[] m_pchString;
// Set m_pchString to null just in case
m_pchString = 0;
}
char* GetString() { return m_pchString; }
int GetLength() { return m_nLength; }
};
The above is a simple string class that allocates memory to hold a string that we pass in. Note that we have not defined a copy constructor or overloaded assignment operator. Consequently, C++ will provide a default copy constructor and default assignment operator that do a shallow copy.
Now, consider the following snippet of code:
MyString cHello("Hello, world!");
{
MyString cCopy = cHello; // use default copy constructor
} // cCopy goes out of scope here
std::cout << cHello.GetString() << std::endl; // this will crash
While this code looks harmless enough, it contains an insidious problem that will cause the program to crash! Can you spot it? Don’t worry if you can’t, it’s rather subtle.
Let’s break down this example line by line:
MyString cHello("Hello, world!");
This line is harmless enough. This calls the MyString constructor, which allocates some memory, sets cHello.m_pchString to point to it, and then copies the string “Hello, world!” into it
MyString cCopy = cHello; // use default copy constructor
This line seems harmless enough as well, but it’s actually the source of our problem! When this line is evaluated, C++ will use the default copy constructor (because we haven’t provided our own), which does a shallow pointer copy on cHello.m_pchString. Because a shallow pointer copy just copies the address of the pointer, the address of cHello.m_pchString is copied into cCopy.m_pchString. As a result, cCopy.m_pchString and cHello.m_pchString are now both pointing to the same piece of memory!
} // cCopy goes out of scope here
When cCopy goes out of scope, the MyString destructor is called on cCopy. The destructor deletes the dynamically allocated memory that both cCopy.m_pchString and cHello.m_pchString are pointing to! Consequently, by deleting cCopy, we’ve also (inadvertently) affected cHello. Note that the destructor will set cCopy.m_pchString to 0, but cHello.m_pchString will be left pointing to the deleted (invalid) memory!
std::cout << cHello.GetString() << std::endl;
Now you can see why this crashes. We deleted the string that cHello was pointing to, and now we are trying to print the value of memory that is no longer allocated.
The root of this problem is the shallow copy done by the copy constructor — doing a shallow copy on pointer values in a copy constructor or overloaded assignment operator is almost always asking for trouble.
Deep copying
The answer to this problem is to do a deep copy on any non-null pointers being copied. A deep copy duplicates the object or variable being pointed to so that the destination (the object being assigned to) receives it’s own local copy. This way, the destination can do whatever it wants to it’s local copy and the object that was copied from will not be affected. Doing deep copies requires that we write our own copy constructors and overloaded assignment operators.
Let’s go ahead and show how this is done for our MyString class:
// Copy constructor
MyString::MyString(const MyString& cSource)
{
// because m_nLength is not a pointer, we can shallow copy it
m_nLength = cSource.m_nLength;
// m_pchString is a pointer, so we need to deep copy it if it is non-null
if (cSource.m_pchString)
{
// allocate memory for our copy
m_pchString = new char[m_nLength];
// Copy the string into our newly allocated memory
strncpy(m_pchString, cSource.m_pchString, m_nLength);
}
else
m_pchString = 0;
}
As you can see, this is quite a bit more involved than a simple shallow copy! First, we have to check to make sure cSource even has a string (line 8). If it does, then we allocate enough memory to hold a copy of that string (line 11). Finally, we have to manually copy the string using strncpy() (line 14).
Now let’s do the overloaded assignment operator. The overloaded assignment operator is a tad bit trickier:
// Assignment operator
MyString& MyString::operator=(const MyString& cSource)
{
// check for self-assignment
if (this == &cSource)
return *this;
// first we need to deallocate any value that this string is holding!
delete[] m_pchString;
// because m_nLength is not a pointer, we can shallow copy it
m_nLength = cSource.m_nLength;
// now we need to deep copy m_pchString
if (cSource.m_pchString)
{
// allocate memory for our copy
m_pchString = new char[m_nLength];
// Copy the parameter the newly allocated memory
strncpy(m_pchString, cSource.m_pchString, m_nLength);
}
else
m_pchString = 0;
return *this;
}
Note that our assignment operator is very similar to our copy constructor, but there are three major differences:
We added a self-assignment check (line 5).
We return *this so we can chain the assignment operator (line 26).
We need to explicitly deallocate any value that the string is already holding (line 9).
When the overloaded assignment operator is called, the item being assigned to may already contain a previous value, which we need to make sure we clean up before we assign memory for new values. For non-dynamically allocated variables (which are a fixed size), we don’t have to bother because the new value just overwrite the old one. However, for dynamically allocated variables, we need to explicitly deallocate any old memory before we allocate any new memory. If we don’t, the code will not crash, but we will have a memory leak that will eat away our free memory every time we do an assignment!
Deep copy in my words
I wrote simple and get crash and did little bit analysis on this issue.
#include<iostream>
using namespace std;
class sample
{
char *c_string;
public:
sample()
{
cout<<" the default constructor is "<<endl;
}
sample(char *p)
{
int i=0;
while(p[i]!='\0')
i++;
c_string =new char[i+1];
i=0;
while(p[i]!='\0')
{
c_string[i]=p[i];
i++;
}
char* getstring()
{
return c_string;
}
~sample()
{
delete[] c_string;
}
};
int main()
{
sample c("hello");
{
sample copy =c; // want to write this line so i have put fix and find the program below
}
cout<<"The value of c"<<c.getstring()<<endl<<endl;
return 0;
}
So I saw program crash i hope you have got understanding from the above article.
The value of c
*** glibc detected *** ./sh: double free or corruption (fasttop): 0x00000000154f1010 ***
======= Backtrace: =========
/lib64/libc.so.6[0x2add27eb82ef]
/lib64/libc.so.6(cfree+0x4b)[0x2add27eb873b]
./sh(__gxx_personality_v0+0x2dc)[0x400ac4]
./sh(__gxx_personality_v0+0x1e4)[0x4009cc]
/lib64/libc.so.6(__libc_start_main+0xf4)[0x2add27e63994]
./sh(__gxx_personality_v0+0x51)[0x400839]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:1a 1365237173 /x/home-new/agoswami/my_work/sh
00601000-00602000 rw-p 00001000 00:1a 1365237173 /x/home-new/agoswami/my_work/sh
154f1000-15512000 rw-p 154f1000 00:00 0 [heap]
2add27497000-2add274b3000 r-xp 00000000 fd:00 786434 /lib64/ld-2.5.so
2add274b3000-2add274b5000 rw-p 2add274b3000 00:00 0
2add274c8000-2add274c9000 rw-p 2add274c8000 00:00 0
2add276b2000-2add276b3000 r--p 0001b000 fd:00 786434 /lib64/ld-2.5.so
2add276b3000-2add276b4000 rw-p 0001c000 fd:00 786434 /lib64/ld-2.5.so
2add276b4000-2add2779a000 r-xp 00000000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add2779a000-2add27999000 ---p 000e6000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add27999000-2add2799f000 r--p 000e5000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add2799f000-2add279a2000 rw-p 000eb000 fd:00 1153587 /usr/lib64/libstdc++.so.6.0.8
2add279a2000-2add279b4000 rw-p 2add279a2000 00:00 0
2add279b4000-2add27a36000 r-xp 00000000 fd:00 786449 /lib64/libm-2.5.so
2add27a36000-2add27c35000 ---p 00082000 fd:00 786449 /lib64/libm-2.5.so
2add27c35000-2add27c36000 r--p 00081000 fd:00 786449 /lib64/libm-2.5.so
2add27c36000-2add27c37000 rw-p 00082000 fd:00 786449 /lib64/libm-2.5.so
2add27c37000-2add27c44000 r-xp 00000000 fd:00 786486 /lib64/libgcc_s-4.1.2-20080825.so.1
2add27c44000-2add27e44000 ---p 0000d000 fd:00 786486 /lib64/libgcc_s-4.1.2-20080825.so.1
2add27e44000-2add27e45000 rw-p 0000d000 fd:00 786486 /lib64/libgcc_s-4.1.2-20080825.so.1
2add27e45000-2add27e46000 rw-p 2add27e45000 00:00 0
2add27e46000-2add27f93000 r-xp 00000000 fd:00 786441 /lib64/libc-2.5.so
2add27f93000-2add28193000 ---p 0014d000 fd:00 786441 /lib64/libc-2.5.so
2add28193000-2add28197000 r--p 0014d000 fd:00 786441 /lib64/libc-2.5.so
2add28197000-2add28198000 rw-p 00151000 fd:00 786441 /lib64/libc-2.5.so
2add28198000-2add2819e000 rw-p 2add28198000 00:00 0
7fffac843000-7fffac858000 rw-p 7ffffffea000 00:00 0 [stack]
ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0 [vdso]
Aborted
sample(const sample& s)
{
int i=0;
while(s.c_string[i]='\0')
i++;
c_string=new char[i+1];
i=0;
while(s.c_string[i]!='\0')
{
c_string[i]=s.c_string[i];
i++;
}
c_string[i]='\0';
}
so i addes this function and able to write this line
but In main I want to write the below line.
sample d;
d=c;
than i have to write the following function.
char* operator=(const sample &s)
{
int i=0;
while(s.c_string[i]!='\0')
i++;
c_string =new char[i+1];
i=0;
while(s.c_string[i]!='\0')
{
c_string[i] =s.c_string[i];
i++;
}
c_string[i]='\0';
return c_string;
}
No comments:
Post a Comment