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

来源:互联网 发布:excel文档修复软件 编辑:程序博客网 时间:2024/05/27 03:26


首先说说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;}


运行结果如下:





 

0 0