Sunday, October 23, 2011

placement new


1. What is placement new? placement new is a overriding version of operator new which I mentioned in my last tips. The placementnew can make it possible to "new" a object on the existing memory. For example, you have a block of memory in your code, which you want to "new" a object on it. You can use placement new.

char* pool=new char[sizeof(Bob)]; //initialize a block of memory
Bob* bob2=new(pool)Bob(2); //"new" a Bob in the pool
You can either "new" Bob on stack or on heap. The example is on the heap.
In fact, the placement new is the step 2 of new operator which I mentioned in my last tips. It just invokes the constructor. Since you cannot invoke the constructor explicitly, you can use placement operator instead. 

2. The problem now is how to release these memory.
I found some materials on the internet which confused me a lot. So I did a sample program myself.

#include<iostream>
#include<new>
using namespace std;

class Bob{
 int index;
 char buffer[7];
public:
 Bob(int i){
  index=i;
  buffer[0]='I';
  buffer[1]=' ';
  buffer[2]='a';
  buffer[3]='m';
  buffer[4]=' ';
  buffer[5]='0'+(char)index;
  buffer[6]='\0';
  cout<<"Bob"<<index<<" is born in constructor"<<endl;
 }
 void Say(){
  cout<<buffer<<endl;
 }
 ~Bob(){
  cout<<"Bob"<<index<<" died in destructor"<<endl;
 }
};

void test(){
 char* pool=new char[sizeof(Bob)]; //on heap
 Bob bob1(1); //on stack
 bob1.Say();
 Bob* bob2=new(pool)Bob(2);
 bob2->Say();
 Bob* bob3=new(&bob1)Bob(3);
 bob3->Say();
 /*
 delete bob2;
 */
 //instead of delete bob2 you can also do the following
 bob2->~Bob();
 delete[] pool;
 //we usaully do the later because placement is usaully used to
 //manage the memory, which means we can declare a block of
 //memory from heap in advance, than manage it, when the program
 //is done, we delete the memory.

 //the stuff in the stack will be released automatically
 //(including destructor)

}

void main(){
 test();
 char a;
 a=getchar();
}

 So 
(1).if the memory is on the heap, we usually invoke the destructor explicitly and then delete the block of memory. This is probably the only situation that need to invoke destructor explicitly.
(2).if the memory is on the stack, let it go. It will invoke your destructor automatically. 


Summary:
This is a good way to manage your memory. Just declare a block of memory, do what you want on it, then delete it.

operator new and new operator and overloading


1. new and delete are very complex, it is worth to do some research on it.
first thing I want to mention is about operator new and new operator. 
new operator is a keyword in the C++ just like sizeof, you can do nothing on it(you cannot try to change them). In fact, when you write the expression
Bob* bob=new Bob();
 C++ do 3 steps for you.
First, they allocate a block of memory for you(actually, you will find that this step is invoke the function new operator) , then invoke the constructor, finally, they return you a pointer. 

We cannot change the whole operator new, but we can do something for each step.

for first step, you can overload the new operator, this is an operator just like + operator,you can overload this operator.
So you can do this in your code:
void *operator new(size_t size); 
 //this is most basic overloading for new operator, placement new 
//and something else will come soon in next tips
for the second step you can override you constructor.

third step is nothing but return a pointer. 

2.Delete operator also does the similar things. But the destructor is invoked before the delete operator. One thing you should never forget, if you overload the new operator, please never forget your delete operator! because who knows what you did in your new operator.


let's look some codes here to know what happened.

#include<iostream>
using namespace std;
class Bob{
 int a;
 int c;
public:
 Bob(int a_,int c_){
  a=a_;
  c=c_;
  cout<<"Bob constructor"<<endl;
 }

 void *operator new(size_t size){
  void* p=malloc(size);
  cout<<"Bob new"<<endl;
  return p;
 }
 void operator delete(void* ptr){
  free(ptr);
  cout<<"Bob died in delete"<<endl;
 }
 ~Bob(){
  cout<<"Bob is killed in destructor"<<endl;
 }
};

void main(int argc,char* arg[]){
 Bob* bob=new Bob(1,2);
 delete bob;
 char a;
 a=getchar();
}
result:

Bob new
Bob constructor
Bob is killed in destructor
Bob died in delete

Saturday, September 24, 2011

转 C++结构体实例和类实例的初始化


结构体实例(包括共用体)和类实例的初始化方法完全相同,二者都可以应用于继承层次中。不同点是结构体(包括共用体)默认成员为public,而类默认成员是private型的。

一、若类和结构体所有数据成员均为public型,可采取如下带花括号形式进行初始化。
    注意:
①        不论值的个数多少,都必须使用花括号定界
②        未指定值的数据成员编译器会自动初始化为默认值
③        这种初始化对象方式,要求所有数据成员必须为public型
④        这种初始化对象方式,要求类中不能编写任何构造函数

struct S {  //class S 效果一样
    int            x;
    unsigned short y;
};
S testS1={100,123};
S testS2={200};//未指定值的数据成员初始化为默认值,这里os2.y=0;
S TestS[4]={ {100,10},
             {200,20},
             {300} };//未指定值的初始化为默认值,os[2].y,os[3].x,os[3].y。

二、若数据成员有private或protected型,或是提供了构造函数,必须使用构造函数来进行初始化。

struct S { //class S可自行试验,结果相同
    private:
        int x;
    public:
        double y;
        S(void){}
        S(int idemo,double ddemo) {x=idemo;y=ddemo;}
        void show(void) {cout<<x<<''/t''<<y<<endl;}
};
S os1;//将调用默认构造函数(无参构造函数)
S os2(1000,2.345);
S os3=S(2000,4.567);
S os[4]={S(10,1.234),S(20,2.234)};//未初始化的将调用默认构造函数。如此时没有默认构造函数会出错。

重要提示:
①        在S os3=S(2000,4.567);语句中,因是声明并初始化os3对象,所以将调用S(int,double)构造函数对os3进行初始化。
②        S os3(2000,4.567); 等价于 S os3=S(2000,4.567);
③        但如果os3已经存在了,S os3(100,1.234);os3=S(2000,4.567),则表示用一个临时对象赋值给os3,将调用operator=,然后系统再释放这个临时产生的对象。系统默认的=运算是将源对象的数据成员的值复制到目标对象中的数据成员中。

三、接受一个参数的构造函数允许使用赋值句法初始化对象。
说明代码如下:

#include <iostream>
using namespace std;
class C {
    private:
        int x;
    public:
        C(int idemo) {x=idemo;}
        void show(void) {cout<<x<<endl;}
};
struct S {
    private:
        int x;
    public:
        S(int idemo) {x=idemo;}
        void show(void) {cout<<x<<endl;}
};
int main(int argc, char *argv[]){
    C oc=1000;//不能企图加花括号
    oc.show();
    S os=2000;//不能企图加花括号
    os.show();
    return EXIT_SUCCESS;
}