对象的构造

  • 从程序设计的角度,对象只是变量,因此:
  1. 在栈上创建对象时,成员变量初始值为随机值
  2. 在堆上创建对象时,成员对象初始值为随机值
  3. 在静态存储区创建对象时,成员变量初始为0值
  4.  一般而言,对象都需要一个确定的初始状态
  5. 解决方案:在类中提供一个public的initialize函数,对象创建后立即调用initialize函数进行初始化
class Test
{
private:
    int i;
    int j;
public:
    void initialize(){i = 0;j = 0;}
    int getI(){return i;}
    int getJ(){return j;}
};
  • 存在的问题
  1. initialize只是一个普通的函数,必须显示调用
  2. 如果未调用initialize函数,运行的结果是不确定的
  • C++中可以定义与类名相同的特殊成员函数
  1. 这种特殊的成员函数叫做构造函数
  2. 构造没有任何返回类型的声明
  3. 构造函数在对象定义时自动被调用
 1 #include <stdio.h>
 2 class Test
 3 {
 4 private:
 5      int i;
 6      int j;
 7 public:
 8      int getI(){ return i; }
 9      int getJ(){ return j; }
10      //构造函数
11      Test()
12      {
13            i = 1;
14            j = 2;
15      }
16 };
17 Test a;
18 int main()
19 {
20      printf("a.i=%d\n",a.getI());
21      printf("a.j=%d\n", a.getJ());
22      Test b;
23      printf("b.i=%d\n", b.getI());
24      printf("b.j=%d\n", b.getJ());
25      Test *pt = new Test;
26      printf("pt->i=%d\n",pt->getI());
27      printf("pt->j=%d\n",pt->getJ());
28      delete pt;
29      return 0;
30 }
31  
32 运行结果:
33 a.i=1
34 a.j=2
35 b.i=1
36 b.j=2
37 pt->i=1
38 pt->j=2
39 请按任意键继续. . .
  • 带有参数的构造函数
  1. 构造函数可以根据需要定义参数
  2. 一个类中可以存在多个重载的构造函数
  3. 构造函数的重载规则遵循C++的重载规则
1 class Test
2 {
3 public:
4     //带参构造函数
5     Test(int v)
6     {
7             //use v to initialize member
8     }
9 }; 
  • 友情提示
  1. 对象定义和对象声明不同
  2. 对象定义:申请对象的空间并调用构造函数
  3. 对象声明:告诉编译器存在这样一个对象
1 //定义对象并调用构造构造函数
2 Test t;
3 int main
4 {
5     //告诉编译器存在名为t的Test对象
6     extern Test t;
7     return 0;
8 }
  • 构造函数的自动调用
 1 #include <stdio.h>
 2 class Test
 3 {
 4 public:
 5      Test()
 6      {
 7            printf("Test()\n");
 8      }
 9      Test(int v)
10      {
11            printf("Test(int v),v=%d\n",v);
12      }
13 };
14 int main()
15 {
16      Test t;      // 调用Test()
17      Test t1(1);  // 调用Test(int v)
18      Test t2 = 2; // 调用Test(int v)
19      t = t2;      //赋值操作,初始化才会调用构造函数
20      int i(100);  //初始化,(初始化和赋值是两个概念,不等价)
21      printf("i=%d\n",i);
22      return 0;
23 }
24 运行结果:
25 Test()
26 Test(int v),v=1
27 Test(int v),v=2
28 i=100
29 请按任意键继续. . .
  • 构造函数的调用
  1. 一般情况下,构造函数在对象定义时被自动调用
  2. 一些特殊情况下,需要手工调用构造函数
  3. 如何创建一个对象数组???
 1 #include <stdio.h>
 2 class Test
 3 {
 4 public:
 5      Test()
 6      {
 7            printf("Test()\n");
 8      }
 9      Test(int v)
10      {
11            printf("Test(int v),v=%d\n",v);
12      }
13 };
14 int main()
15 {
16      Test t;             // 调用Test()
17      Test t1(1);         // 调用Test(int v)
18      Test t2 = 2;       // 调用Test(int v)
19      t =  t2;          //赋值操作,初始化才会调用构造函数
20      int i(100);       //初始化,(初始化和赋值是两个概念,不等价)
21      printf("i=%d\n",i);
22      Test Ta[3] = {Test(),Test(1),Test(2)}; //手动调用构造函数
23      Test ta = Test(3);
24      return 0;
25 }
26
运行结果:

pi@raspberrypi:~ $ g++ main.cpp
pi@raspberrypi:~ $ ./a.out
Test()
Test(int v),v=1
Test(int v),v=2
i=100
Test()
Test(int v),v=1
Test(int v),v=2
Test(int v),v=3

  • 两个特殊的构造函数
  1. 无参构造函数:没有参数的构造函数,当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空。
  2. 拷贝构造函数:参数为const class_name&的构造函数,当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。
 1 #include <stdio.h>
 2 class Test
 3 {
 4 private:
 5      int i;
 6      int j;
 7 public:
 8      int getI()
 9      {
10            return i;
11      }
12      int getJ()
13      {
14            return j;
15      }
16      //编译器会在没有提供一个构造函数时,才会提供无参构造函数
17      Test()
18      {
19      }
20      //拷贝构造函数,当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。
21      Test(const Test&t)
22      {
23            i = t.i;
24            j = t.j;
25      }
26 };
27 int main()
28 {
29      Test T1;
30      Test T2 = T1;
31      printf("T1.I=%d,T1.J=%d\n",T1.getI(),T1.getJ());
32      printf("T2.I=%d,T2.J=%d\n", T2.getI(), T2.getJ());
33      return 0;
34 }
运行结果:

pi@raspberrypi:~ $ g++ main.cpp
pi@raspberrypi:~ $ ./a.out
T1.I=66536,T1.J=0
T2.I=66536,T2.J=0



  • 拷贝构造函数的意义
  1. 兼容C语言的初始化方式
  2. 初始化行为能够符合预期的逻辑(初始化会牵涉到拷贝构造函数的调用
  • 拷贝构造函数的意义
  1. 浅拷贝:拷贝后对象的物理状态相同
  2. 深拷贝:拷贝后对象的逻辑相同
  3. 编译器提供的拷贝构造函数只进行浅拷贝
#include <stdio.h>
 
 
class Test
{
private:
    int i;
    int j;
    int *p;
public:
    int getI()
    {
        return i;
    }
    int getJ()
    {
        return j;
    }
    int* getp()
    {
        return p;
    }
    //编译器会在没有提供一个构造函数时,才会提供无参构造函数
    //Test()
    //{
 
 
    //}
    Test(int v)
    {
        i = 1;
        j = 2;
        p = new int;
        *p = v;
    }
    //拷贝构造函数,当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。
    Test(const Test&t)
    {
        i = t.i;
        j = t.j;
        p = new int;
        *p = *t.p;
    }
    void free()
    {
        delete p;
    }
};
int main()
{
    Test T1(3);
    Test T2(T1);
    printf("T1.I=%d,T1.J=%d,*T1.p=%d\n",T1.getI(),T1.getJ(),*T1.getp());
    printf("T2.I=%d,T2.J=%d,*T2.p=%d\n",T2.getI(), T2.getJ(),*T2.getp());
    return 0;
}
运行结果:

pi@raspberrypi:~ $ g++ main.cpp
pi@raspberrypi:~ $ ./a.out
T1.I=1,T1.J=2,*T1.p=3
T2.I=1,T2.J=2,*T2.p=3

 
  • 什么时候进行深拷贝?
  1. 对象中有成员指代了系统中的资源
  2. 成员指向了动态内存空间
  3. 成员打开了外存中的文件
  4. 成员使用了系统中的网络端口
  • 问题分析:
当没有进行深拷贝时,程序将默认进行浅拷贝,程序指向同一个内存空间
浅拷贝
 
 
深拷贝(手工提供构造函数)
 
 
 
  • 一般性原则
  1. 自定义拷贝构造函数,必然需要实现拷贝构造函数
  • 小结
  1. C++编译器会默认提供构造函数
  2. 无参构造函数用于定义对象的默认的初始状态
  3. 拷贝构造函数在创建对象时拷贝对象的状态
  4. 对象的拷贝有浅拷贝和深拷贝两种方式:浅拷贝使得对象的物理状态相同,深拷贝使得对象的逻辑状态相同。
 
 
 
 
posted @ 2020-01-13 00:04  认真做个普通人  阅读(357)  评论(0编辑  收藏  举报