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. 【NOIP2016提高A组集训第13场11.11】最大匹配

    题目 mhy12345学习了二分图匹配,二分图是一种特殊的图,其中的点可以分到两个集合中,使得相同的集合中的点两两没有连边. 图的"匹配"是指这个图的一个边集,里面的边两两不存在公 ...

  2. CSP-S2019游记&拆塔记

    不是拆广州塔 Day -inf 四套NOI模拟降智 Day0 拆了一发新新 本来想复习小圆脸结果拆了3h最后还没带任意门 没有帘子可还行 第一天由于没发现被子可以抽出来就没睡好 Day1 8:30开考 ...

  3. 【java工具类】生成二维码

    /** * 生成二维码图片 * @param text 扫描二维码后跳转的url * @param width 图片宽度 * @param height 图片高度 * @param filePath ...

  4. luogu 4725 【模板】多项式对数函数(多项式 ln)

    $G(x)=ln(A(x))$ $G'(x)=ln'(A(x))A'(x)=\frac{A'(x)}{A(x)}$     由于求导和积分是互逆的,所以对 $G$ 求积分,即 $G(x)=\int\f ...

  5. golang rabbitmq实践 (二 实现简单的消息收发)

    1:驱动 本来打算自己写一个驱动的,后来发现github上面已经有了,那我就直接拿现成的了, 驱动采用 github.com/streadway/amqp ,直接import就可以啦! 2:excha ...

  6. js控制页面每次滚动一屏,和楼梯效果

    我最近在做我们公司官网的改版,产品中心就是每次滚一屏的,我觉得加上楼梯更方便用户浏览,就随便写了个demo, 先来看看结构,都很简单的 <!--楼梯--> <ul class=&qu ...

  7. SQL简介及分类

    SQL (Structured Query Language) 结构化查询语言,定义了所有操作关系型数据库的规则,只要是关系型数据库都需要用SQL语句: 语法: 一行可以有多个SQL语句,以分号结尾: ...

  8. 配置kubernetes.client的参数遇到的坑

    配置kubernetes.client遇到的一些坑: 一,job-name不能重名,如果job-name已经有了,再创建job,则会发生冲突cliflict 这样将会报以下错误:Reason : Co ...

  9. Route53 health check与 Cloudwatch alarm 没法绑定

    原因 即使在控制台创建 创建的alarm会在us-east-1 不会再其他区域,目前route53 metric 在其他区域不存在. 所以使用cloudformation 创建 route53 hea ...

  10. 关于vue给对象新增属性页面不会动态更新

    不知道大家有没有遇到过这个问题,当我们给data里边声明或者已经赋值过的对象或者数组,添加新的属性时,如果更新此属性的值是不会动态更新视图的. $set 看以下实例: 我们开始给drug_list追加 ...