C++ 内存管理(memory Management) part1


首先说说constructors(建构子)这个类的特殊的成员函数。

当一个类的instance被创建时, 会呼叫建构子以对对象的数据初始化。

例如如下例:

声明一个整数的类:

#include <iostream>
using namespace std;

class Integer {
   private:
      int val;
   public:
      Integer() {
         val = 0;
         cout << "default constructor" << endl;
      }
}; //分号不能少

int main() {
   Integer i;
   return 0;
}


运行结果为:

 

不难发现, 上述主程序中子调用了默认建构子。

当我们创建an array of objects的时候, 默认建构子会逐一调用(必须有默认建构子, 才可以创建an array of objects)。

#include <iostream>
using namespace std;

class Integer {
   private:
      int val;
   public:
      Integer() {
         val = 0;
         cout << "default constructor" << endl;
      }
}; //分号不能少

int main() {
   Integer arr[3];
   return 0;
}


程序运行结果为:

 

当在一个类中使用了另一个类声明的对象作为其成员变量, 此时城建这个类的对象的时候, 也会调用内部那个嵌入类的constructor, 以便对其初始化。

例:

#include <iostream>
using namespace std;

class Integer {
   private:
      int val;
   public:
      Integer() {
         val = 0;
         cout << "Integer default constructor" << endl;
      }
}; //分号不能少

class IntegerWrapper {
   private:
      Integer val;
   public:
      IntegerWrapper() {
         cout << "IntegerWrapper default constructor" << endl;
      }
};

int main() {
   IntegerWrapper q;
   return 0;
}


运行结果为:

 

Constructor 也可以有参数, 但是此时该建构子不是默认的construtor, 以便传递参数更好地初始化。 此时, 我们必须定义(写明)默认的建构子。 否则默认的建构子 not available。 如果不写明默认建构子, 一个缺点就是无法什么 array of objects  without initializing了。

例如, 下面的两个程序是错误的:

#include <iostream>
using namespace std;

class Integer {
   private:
      int val;
   public:
      Integer(int v) {
         val = v;
         cout << "Integer default constructor" << endl;
      }
}; //分号不能少


int main() {
   Integer i(3);//ok
   Integer j;// error
   return 0;
}


上述代码会出错, 再例如:

#include <iostream>
using namespace std;

class Integer {
   private:
      int val;
   public:
      Integer(int v) {
         val = v;
      }
}; //分号不能少


int main() {
   Integer a[] = {Integer(2), Integer(5)};//ok, initializing
   Integer b[2];// error, without initializing
   return 0;
}


下面修改如上的代码, 两种办法, (1)单独写一个默认的建构子(默认的就是没有参数的)

(2)采用函数的default 用一个建构子代表两个建构子(一个有参数, 一个是无参数的默认建构子):

#include <iostream>
using namespace std;

class Integer {
   private:
      int val;
   public:
      Integer(int v) {
         val = v;
      }

      Integer() {
         val = 0;
      }
}; //分号不能少


int main() {
   Integer a[] = {Integer(2), Integer(5)};//ok, initializing
   Integer b[2];//okay 
   return 0;
}


上述的代码是correct的。

 

this 指针

当一个method的参数的名字和类的一个field同名的时候, 如何去区分呢, 这就用到了this指针。 this指向当前调用这个方法的对象的位置。

例如:

class Integer {
   private:
      int val;
   public:
      Integer(int val = 0) { //default parameter
         this -> val = val;
      }
      void setVal(int val) {
         this -> val = val;
      }

}; //分号不能少

 

接下来, 说说Scoping and Memory

 

每当我们声明一个新的变量的时候(int x), 编译器就会给这个变量x 分配(allocate)一个内存。 当这个变量go out of scope 的时候, 这个内存就会被释放掉(be freed up), 以便这个内存位置可以分配给其他变量。

例如:

int main() {
   if (true) {
      int x = 5; //declare a variable, allocate memory
   } 
   // x now out of scope, memory it used to occupy can be reused
}


一句话, 当一个variable goes out of scope, that memory is no longer guaranteed to store the variables value。

在举一个例子:

int main() {
   int *p;
   if (true) {
      int x;
      p = &x;
   }
   cout << *p << endl; // ???, x is out of scope, so it will get wrong answer
   return 0;
}


上个例子会出错。

具体分析内存分配如下面几幅图:

 

 

上图中, 最后一幅图不难看出, 变量x goes out of scope, 所以该内存(用于存储5的memory)被释放掉了。 也就是说, 此时指针p变成了一个dangling pointer(也就是说指针指向的memory的contents 是未定义的(undefined))。 

 

A problematic Task

Implement a function which returns a pointer to some memory containing the integer 5.

下面的实现的函数是incorrect的,原因是变量x 是在function scope 中, 该函数返回的时候, x 就会go out of scope。 所以此时返回的指针就变成了一个dangling pointer。

int *getPtrToFive() {
   int x = 5;
   return &x;
}

int main() {
   int *p = getPtrToFive();
   cout << *p << endl; //??
}



 

 

 

 

 

 

上述函数的实现方法是不正确的。 走出这个dilemma的方法就是我们动态的分配内存。这种动态分配的内存会一直remian 下去直到我们manually de-allocate it。

动态分配内存用到了new operator,此时返回一个pointer 指向the newly allocated memory.  如下句:

int *x = new int;

 

NOTE:

——如果使用 int x; 分配的内存区域在the stack (栈)中

——如果使用 new int; 分配的内存区域在the heap (堆)中。

 

要释放由 new operator 分配在heap 中的内存, 必须使用delete operator 手动释放的。 (这两个运算符需要配套使用, 使用完毕后, 用delete释放动态内存)。

int *getPtrToFive() {
   int *x = new int;
   *x = 5;
   return x;
}

int main() {
   int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
   cout << *p << endl; //5
   delete p;
}

 

注意, 如果使用完后, 不用delete 去deallocate由new 分配的动态内存, 那么我们编写的application 就会waste memory。

如下例:

int *getPtrToFive() {
   int *x = new int;
   *x = 5;
   return x;
}

int main() {
   for (int i = 0; i < 3; ++i) {
      p = getPtrToFive();
      cout << *p << endl;
   }
}


具体分析如下:

上述的程序错误的原因在于没有deallocate 分配的动态内存。

 

再比如, 下面的例子, 虽然delete了, 但是只是deallocate 最后一次分配的动态、内存:

int *getPtrToFive() {
   int *x = new int;
   *x = 5;
   return x;
}

int main() {
   for (int i = 0; i < 3; ++i) {
      p = getPtrToFive();
      cout << *p << endl;
   }
   delete p; 
}


 

 

上述的语句时不正确的, 因为它导致了memory leak(内存泄露)。 要想修正这个错误, 从而避免内存泄露, 把delete的语句放在loop内:

 

int *getPtrToFive() {
   int *x = new int;
   *x = 5;
   return x;
}

int main() {
   for (int i = 0; i < 3; ++i) {
      p = getPtrToFive();
      cout << *p << endl;
      delete p;
   }
   
   return 0;
}

 

注意, 删除heap中的动态内存之后, 就不要再使用分配的内存了:

int *getPtrToFive() {
   int *x = new int;
   *x = 5;
   return x;
}

int main() {
   int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
   delete p;
   cout << *p; // error
}

必须在使用之后, 不再需要了, 才删除:

int *getPtrToFive() {
   int *x = new int;
   *x = 5;
   return x;
}

int main() {
   int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
   cout << *p << endl; //5
   delete p;
   return 0;
}


注意, 同一个内存空间删除两次:

int *getPtrToFive() {
   int *x = new int;
   *x = 5;
   return x;
}

int main() {
   int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
   cout << *p << endl; //5
   delete p; delete p; // error
   return 0;
}

 

只有用new 分配的动态内存才可以使用delete operator:

int main() {
   int x = 5;
   int *xPtr = &x;
   cout << *xPtr << endl;
   delete xPtr; //error
   return 0;
}

上面是错误的, 应该使用:

int main() {
   int x = 5;
   int *xPtr = &x;
   cout << *xPtr << endl;
}


Allocating Arrays

在stack 中声明数组时(分配内存), 我们必须指定数组的size 为常量, 不能为变量。也就是说:

int arr[SIZE];// size 必须为常量

例如, 下面的语句时错误的:

int numItems;
cout << "How many items?";
cin >> numItems;
int arr[numItems]; // not allowed


要想实现动态的分配数组的内存, 必须使用 new [] , 这时候, 数组可以有variable size, 例如:

int numItems;
cout << "How many items?";
cin >> numItems;
int *arr= new int[numItems];


具体分析如下:

 

删除动态数组的内存, 使用的是 delete[], 如下:

 

int numItems;
cout << "How many items?";
cin >> numItems;
int *arr= new int[numItems];
delete[] arr;


下面举个例子:

#include <iostream>
using namespace std;
int main() {
   int numItems;
   cout << "How many items?";
   cin >> numItems;
   int *arr = new int[numItems];
   for (int i = 0; i < numItems; ++i) {
      cout << "Enter item" << i << ":";
      cin >> arr[i];
   }
   for (int i = 0; i < numItems; ++i) {
      cout << arr[i] << endl;
   }
   delete[] arr;
   return 0;
}



运行结果如下:





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值