1,对象的构造在实际工程开发当中是相当重要的,C++ 中使用类就要创建对象,这 就涉及了对象的构造,本节课讲解对象的构造和内存操作方面的问题;

2,实际工程开发中,bug 产生的根源,必然的会有内存操作的问题,所以对象的构 造牵涉了内存的操作,则是课程的重点和难点;

3,两个特殊的构造函数(同类名相同的无返回值的可自动调用的函数,这里也就是 说明了没有赋值操作符函数):

1,无参构造函数:

1,没有参数的构造函数;

1,没有参数的构造函数就是无参构造函数;

2,当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空(这是无参构造函数特殊之处);

1,无参构造函数是必须要存在的,因为使用类就要创建对象,创建对象就涉及构造函数的调用,如果定义一个类,它里面没有任何构造函数时,为了保证能够使用这个类来创建对象,编译器为我们提供了一个默认的构造函数,并且让这个默认的构造函数函数体为空;

2,类中已经定义了一个构造函数(包括拷贝构造函数),编译器便不会为我们提供默认的无参构造函数;

2,拷贝构造函数:

1,参数为 const class_name& 的构造函数;

1,和对象的拷贝和复制相关;

2,const class_name& 参数出现在构造函数中,则必然是拷贝构造函数;

2,当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值赋值;

1,类中只定义了无参构造函数后,任然会提供拷贝构造函数;

4,特殊的构造函数编程实验:

1,代码示例:

 #include <stdio.h>

 class Test
{
private:
int i;
int j;
public:
int getI()
{
return i;
} int getJ()
{
return j;
} /*Test(const Test& t) // 编译器默认提供;
{
i = t.i; // 编译器做的工作;
j = t.j;
} Test() // 编译器默认提供;
{
}*/
}; class T // 这个类中至少有一个无参构造函数;
{
}; int main()
{
Test t; // 编译通过,C++ 编译器提供了无参的默认构造函数,即屏蔽的第二个函数,直接加载在类函数体后面的三行代码; Test t1; // 这里打印随机值;
Test t2; // 这里打印随机值; int i = ;
int j = i; // C 语言中可以存在这样的初始化方式,面向对象也可以(要兼容 C 语言语法),用另一个对象初始化新定义的对象; Test t2 = t1; // 这里打印相同的随机值,这里是对象的赋值,和前面用常量值的赋值来调用构造函数是不同的,前面调用的只是有参构造函数中的非拷贝构造函数,说到底还是构造函数的重载问题,只是这里重载的是对象,而上一节重载的是变量而已; printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ());
printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ()); return ;
}

2,实验结果说明:

1,编译器提供的构造函数只有两种,一种是无参构造函数,一种是拷贝构造函数;

2,编译器提供的拷贝构造函数仅仅是对成员变量进行简单的复制;

5,拷贝构造函数的意义:

1,兼容 C 语言的初始化方式;

1,这里初始化是对象的初始化,会牵涉到拷贝构造函数的调用;

2,利用已经存在的对象来创建另一个新的对象,进而使得这两个对象那个是一样的;

2,初始化行为能够符合预期的逻辑;

1,预期的逻辑是两个对象的状态是一模一样的;

6,初始化的构造函数调用问题总结:

1,初始化会调用构造函数;

2,构造函数的调用会以重载的方式调用,不管实参是类的对象还是变量;

3,为了兼容 C 语言中的赋值初始化方式,C++ 也提供了赋值初始化的方式;

4,对基础变量的重载调用普通的构造函数,对对象的重载调用拷贝构造函数(构造函数的一种特殊名称而已,但是编译器会在没有这个构造函数时候默认的创建这个函数);

5,拷贝构造函数

7,拷贝构造函数的意义:

1,浅拷贝:

1,拷贝后对象的物理状态相同;

1,面向对象里面,最根本的还是会牵涉到内存问题;

2,浅拷贝使得对象的物理状态相同,单纯的进行值的复制;

3,复制过后,两个对象在内存当中的状态一模一样;

2,编译器提供的拷贝构造函数只进行浅拷贝;

1,简单的成员的复制,所以是浅拷贝;

2,深拷贝:

1,拷贝后对象的逻辑状态相同;

8,对象的初始化编程实验:

 #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(const Test& t) // 手工定义拷贝构造函数;这样得到了深拷贝,因为已经深入到对应的堆空间的里面的值,所以叫深拷贝;
{
i = t.i;
j = t.j;
p = new int; // p 指向新的堆空间地址;p 的指针值不能够复制了,要到堆空间里面申请; *p = *t.p; // 将指向地址当中的值重新指定;申请后将 t 对象中的 p 指针指向的值拿出来,赋值到 p 所指向的堆空间;
}
/*
Test(const Test& t) // 未有人为定义上面的拷贝构造函数的时候,编译器提供的拷贝构造函数;
{
i = t.i;
j = t.j;
p = t.p;
}
*/
Test(int v)
{
i = ;
j = ;
p = new int; *p = v; // 这个程序其构造函数仅仅是想将参数值存储到某个堆空间中,这个堆空间可以是不同的,因此拷贝构造函数也做同样的事就可以了;所以在写拷贝构造函数的时候,要看其构造函数要表达的意义,满足即可;
} void free()
{
delete p;
}
}; int main()
{
Test t1();
/*
Test t = t1; // 未有提供拷贝构造函数的时候,下面打印的成员函数值 完全相同,包括指针的地址值;t 和 t1 的 p 指向了相同的堆空间的东西,这在对象释放堆空间中的内存时,内存错误;物理状态指的是我们的对象占据的内存当中他们的每个字节是否相同,此时物理状态相同; printf("t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP());
printf("t.i = %d, t.j = %d, t.p = %p\n", t.getI(), t.getJ(), t.getP()); t1.free();
t.free(); // 未有提供拷贝构造函数的时候,指向相同堆内存空间,重复释放堆空间,内存错误;
*/ /*
Text t2 = t1; // t1 产生时其 p 指针指向堆空间的某个地址,使用 t1 初始化 t2 的时候,t2 的 p 指针也应该指向堆空间里面的内存地址,并且应该是一个不同的内存地址;这样不违背拷贝构造的意义,见提供的拷贝构造函数;触发如上所述的拷贝构造函数的调用,此时其实参为 t1;
*/ Test t2(t1); // 同上面的代码,只是不同的表述,t1 会被参数 t 引用;下面打印的指针值不是相同的,但是 p 中指向的值是一样的,这样状态就一致了;此时逻辑状态相同,根据程序上下文,仅仅需要 t1 和 t2 中 p 指针所指向的值是一样的,这是逻辑状态; printf("t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP());
printf("t2.i = %d, t2.j = %d, t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP()); 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()); t1.free();
t2.free(); return ;
}

9,什么时候需要进行深拷贝?

1,对象中有成员指代了系统中的资源(唯一准则):

1,成员指向了动态内存空间;

1,内存是系统资源之一;

2,成员打开了外存中的文件;

1,类的成员打开了系统当中的一个文件,它是系统资源之一;

3,成员使用了系统中的网络端口;

1,网络端口也是系统资源之一;

4,...;

2,问题分析:

3,一般性原则:

1,自定义拷贝构造函数,必然需要实现深拷贝;

1,要自定义拷贝构造函数,就要考虑是不是要做一个深拷贝;

2,如果是,要考虑深拷贝是否会在多个对象之间造成问题(比如多次释放堆空间);

3,如果不是,要考虑为何要自定义拷贝构造函数而不使用编译器默认提供的拷贝构造函数;

10,数组类的改进编程实验:

1,IntArray.h 文件:

 #ifndef _INTARRAY_H_
#define _INTARRAY_H_ class IntArray
{
private:
int m_length;
int* m_pointer;
public:
IntArray(int len);
IntArray(const IntArray& obj);
int length();
bool get(int index, int& value);
bool set(int index ,int value);
void free();
}; #endif

2,IntArray.cpp 文件:

 #include "IntArray.h"

 IntArray::IntArray(int len)
{
m_pointer = new int[len]; // 在构造函数中申请了堆空间的内存,因此要给数组类提供一个拷贝构造函数; for(int i=; i<len; i++)
{
m_pointer[i] = ;
} m_length = len;
} IntArray::IntArray(const IntArray& obj)
{
m_length = obj.m_length; // length 直接赋值; m_pointer = new int[obj.m_length]; // pointer 要到堆空间申请内存,大小和初始化对象的一样,加上下面,完成了深拷贝; for(int i=; i<obj.m_length; i++) // 完成数组元素的赋值和复制;
{
m_pointer[i] = obj.m_pointer[i];
}
} int IntArray::length()
{
return m_length;
} bool IntArray::get(int index, int& value)
{
bool ret = ( <= index) && (index < length()); if( ret )
{
value = m_pointer[index];
} return ret;
} bool IntArray::set(int index, int value)
{
bool ret = ( <= index) && (index < length()); if( ret )
{
m_pointer[index] = value;
} return ret;
} void IntArray::free()
{
delete[]m_pointer;
}

3,IntArray 类的使用:

 #include <stdio.h>
#include "IntArray.h" int main()
{
IntArray a(); for(int i=; i<a.length(); i++)
{
a.set(i, i + );
} for(int i=; i<a.length(); i++)
{
int value = ; if( a.get(i, value) )
{
printf("a[%d] = %d\n", i, value);
}
} IntArray b = a; // 用 a 对象初始化 b 对象; for(int i=; i<b.length(); i++)
{
int value = ; if( b.get(i, value) )
{
printf("b[%d] = %d\n", i, value);
}
} a.free();
b.free(); return ;
}

11,小结:

1,C++ 编译器会默认提供构造函数;

2,无参构造函数用于定义对象的默认初始化状态;

3,拷贝构造函数在创建对象时拷贝对象的状态;

4,对象的拷贝有浅拷贝和深拷贝两种方式:

1,浅拷贝使得对象的物理状态相同;

2,深拷贝使得对象的逻辑状态相同;

C++中的深拷贝和浅拷贝构造函数的更多相关文章

  1. 内功心法 -- Java中的深拷贝和浅拷贝

    写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------这篇博客主要来谈谈" ...

  2. 浅谈Java中的深拷贝和浅拷贝(转载)

    浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.csdn.net/tounaobun/article/details/8491392 假如说你想复制一个简单变量.很简单: ...

  3. C语言中的深拷贝和浅拷贝

    //C语言中的深拷贝和浅拷贝 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #inc ...

  4. 浅谈Java中的深拷贝和浅拷贝

    转载: 浅谈Java中的深拷贝和浅拷贝 假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(bool ...

  5. **Python中的深拷贝和浅拷贝详解

    Python中的深拷贝和浅拷贝详解   这篇文章主要介绍了Python中的深拷贝和浅拷贝详解,本文讲解了变量-对象-引用.可变对象-不可变对象.拷贝等内容.   要说清楚Python中的深浅拷贝,需要 ...

  6. javascript中的深拷贝与浅拷贝

    javascript中的深拷贝与浅拷贝 基础概念 在了解深拷贝与浅拷贝的时候需要先了解一些基础知识 核心知识点之 堆与栈 栈(stack)为自动分配的内存空间,它由系统自动释放: 堆(heap)则是动 ...

  7. JavaScript中的深拷贝和浅拷贝!【有错误】还未修改!请逛其他园子!

    JavaScript中的深拷贝和浅拷贝! 浅拷贝 1.浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用.{也就是拷贝的是地址!简而言之就是在新的对象中修改深层次的值也会影响原来的对象!} // 2.深 ...

  8. 001 说说Python中的深拷贝和浅拷贝

    在Python编程中忽略深拷贝和浅拷贝可能会造成未知的风险. 比如我们打算保存一份原始对象的副本作为上一状态的记录,此后修改原始对象数据时,若是副本对象的数据也发生改变,那么这就是一个严重的错误. 注 ...

  9. js中的深拷贝与浅拷贝

    对象的深拷贝于浅拷贝 对于基本类型,浅拷贝过程就是对值的复制,这个过程会开辟出一个新的内存空间,将值复制到新的内存空间.而对于引用类型来书,浅拷贝过程就是对指针的复制,这个过程并没有开辟新的堆内存空间 ...

随机推荐

  1. artTemplate字符串模板

    1.官网:http://aui.github.io/art-template/

  2. ROM和RAM的内存详细说明

    1.首先是ROM 的读取是需要提前两个地址的读取,所以要想读取0地址的数据,你需要给地址是2 2.关于宽度,深度的计算 假设我们要存取如下取模的数据,该模的设置口语描述为:这是显示的2个字节,其中一个 ...

  3. jquery animated选择器 语法

    jquery animated选择器 语法 作用::animated 选择器选取当前的所有动画元素.直线电机参数 语法:$(":animated") jquery animated ...

  4. PHP 大文件上传,支持断点续传,求具体方案、源码或者文件上传插件

    文件夹数据库处理逻辑 publicclass DbFolder { JSONObject root; public DbFolder() { this.root = new JSONObject(); ...

  5. CQOI2010 传送带

    题目链接:戳我 分别枚举线段AB上的出发点,和线段CD上的到达点,然后时间直接计算,取min就可以了. 但是这样子显然会T飞,(相当于1e5的平方吧?)所以我们进一步考虑性质. 然后打表(或者感性理解 ...

  6. MySQL_约束

    MySQL中约束的作用是对表中的数据进行限定,保证数据的正确性,完整性,有效性. 分类:(1)主键约束 primary key(2)非空约束 not NULL (3)唯一约束 unique (4)外键 ...

  7. Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?

    if first time to install docker, be noted the docker engine started as root copied from: http://blog ...

  8. Python可变数据类型list填坑一则

    前提概要 最近写业务代码时遇到一个列表的坑,在此记录一下. 需求 现在有一个普通的rule列表: rule = [["ID",">",0]] 在其他地方经 ...

  9. [论文理解] LFFD: A Light and Fast Face Detector for Edge Devices

    LFFD: A Light and Fast Face Detector for Edge Devices 摘要 从微信推文中得知此人脸识别算法可以在跑2K图片90fps,仔细一看是在RTX2070下 ...

  10. Vue项目移动端滚动穿透问题

    概述 今天在做 Vue 移动端项目的时候遇到了滚动穿透问题,在网上查资料后,选取了我觉得最好的方法,记录下来供以后开发时参考,相信对其他人也有用. 上层无需滚动 如果上层无需滚动的话,直接屏蔽上层的 ...