在类里面成员函数的初始值是多少了?(取决于创建对象的位置,是在堆、栈、还是在静态存储区中创建。)

  例如:  

#include <stdio.h>

class Test
{
private:
int i;
int j;
public :
int get_i(void) {return i;}
int get_j(void) {return j;}
}; Test Ta;//在静态存储区中定义 Test类 int main(int argc, char *argv[])
{
printf("Ta.i = %d\n",Ta.get_i());//Ta.i = 0
printf("Ta.j = %d\n",Ta.get_j());//Ta.j = 0 Test Tb;//在栈上定义类
printf("Tb.i = %d\n",Tb.get_i());//Tb.i = 随机数
printf("Tb.j = %d\n",Tb.get_j());//Tb.j = 随机数 Test *Tc = new Test;//在堆上定义类
printf("Tc->i = %d\n",Tc->get_i());//Tc.i = 随机数
printf("Tc->j = %d\n",Tc->get_j());//Tc.i = 随机数 return ;
}

  运行结果:  

Ta.i =
Ta.j =
Tb.i =
Tb.j =
Tc->i =
Tc->j =

  可以看出,对象只是变量,所以在不同的地方定义变量,所的到的初始值也不同。

  在堆上定义:为随机数

  在栈上定义:为随机数

  在静态存储区上定义:因为静态存储区中变量默认为0 ,所以为0

这样在不同地方定义初始值就会不同,这样是不允许的所以我们需要对变量进行初始化。这就引入了类的构造函数。

构造函数:

  构造函数特点:

    1、构造函数没有任何返回类型的声明。

    2、构造函数在定义的时候被自动调用。

    例如:    

#include <stdio.h>

class Test
{
private:
int i;
int j;
public :
int get_i(void) {return i;}
int get_j(void) {return j;}
Test()
{
printf("Test()\n");
i = ; j = ;
}
}; Test Ta; int main(int argc, char *argv[])
{
printf("Ta.i = %d\n",Ta.get_i());
printf("Ta.j = %d\n",Ta.get_j()); Test Tb;
printf("Tb.i = %d\n",Tb.get_i());
printf("Tb.j = %d\n",Tb.get_j()); Test *Tc = new Test;
printf("Tc->i = %d\n",Tc->get_i());
printf("Tc->j = %d\n",Tc->get_j()); return ;
}

  在类中加入构造函数。

  运行结果:  

Test()
Ta.i =
Ta.j =
Test()
Tb.i =
Tb.j =
Test()
Tc->i =
Tc->j =

  可以看出每次定义都调用了一次构造函数。

一个类中可以有多个构造函数构成重载,重载的概念在类中同样适用。

  例如:

  

#include <stdio.h>

class Test
{
private:
int i;
int j;
public :
int get_i(void) {return i;}
int get_j(void) {return j;}
Test()
{
printf("Test()\n");
i = ; j = ;
}
Test(int v)
{
printf("Test(int v);v = %d\n",v);
i = ; j = ;
}
}; int main(int argc, char *argv[])
{
Test Ta;
printf("Ta.i = %d\n",Ta.get_i());
printf("Ta.j = %d\n",Ta.get_j()); Test Tb();
printf("Tb.i = %d\n",Tb.get_i());
printf("Tb.j = %d\n",Tb.get_j()); Test *Tc = new Test();
printf("Tc->i = %d\n",Tc->get_i());
printf("Tc->j = %d\n",Tc->get_j()); return ;
}

  运行结果:  

Test()
Ta.i =
Ta.j =
Test(int v);v =
Tb.i =
Tb.j =
Test(int v);v =
Tc->i =
Tc->j =

从结果中可以看出:

 Test Ta;调用的是 Test() 这个构造函数。
Test Tb(10);和 Test *Tc = new Test(30);调用的是   Test(int v) 这个构造函数。

注意:对象的定义与对象的声明是不同的。例如变量的定义与变量的声明也是不同的。
  对象定义:声明对象的空间并调用构造函数。
  对象的声明:告诉编译器存在这样的一个变量。
构造函数的手动调用:
  一般来说构造函数在定义对象的时候被自动调用,但是在一些特殊情况下需要手动调用。
  例如构造对象数组。
实验:创建一个数组类解决数组的安全性问题。
  1、创建Intarray.h
    
#ifndef __INTARRAY_H
#define __INTARRAY_H
class intArray
{
private:
int arrayLenght;
int *Parray;
public:
intArray (int lenght);//构造函数
bool changeArray(int index,int val);//修改数组中的元素
int getLenght(void);//获取数组长度
bool getArrayData(int index,int& val);//获取数组中的元素
void free();
}; #endif
  2、创建Intarray.cpp
  
#include "intArray.h"

intArray::intArray (int lenght)//构造函数
{
Parray = new int[lenght];//创建数组空间
for(int i=; i<lenght; i++)//初始化
Parray[i] = ;
arrayLenght = lenght;
}
bool intArray::changeArray(int index,int val)//修改数组中的元素
{
bool ret = (index>=)&&(index < arrayLenght);//判断是否越界
if(ret)
{
Parray[index] = val;
}
return ret;
}
int intArray::getLenght(void)//获取数组长度
{
return arrayLenght;
}
bool intArray::getArrayData(int index, int& val)//获取数组中的元素
{
bool ret = (index>=)&&(index < arrayLenght);//判断是否越界
if(ret)
{
val = Parray[index] ;
}
return ret;
} void intArray::free()//
{
delete[] Parray;
}

   3、创建main.cpp

#include <stdio.h>
#include "intArray.h" int main(int argc, char *argv[])
{
int temp ;
intArray TestArray();
for(int i=; i<TestArray.getLenght();i++)
TestArray.changeArray(i,i);
for(int i=; i<TestArray.getLenght();i++)
{
if(TestArray.getArrayData(i,temp))
printf("getArrayData(%d) = %d\n",i,temp);
} TestArray.free();
return ;
}

运行结果:

getArrayData() =
getArrayData() =
getArrayData() =
getArrayData() =
getArrayData() =
getArrayData() =
类中的特殊构造函数:
  1、无参构造函数。(当类中没有定义任何构造函数时,编译器会默认的提供一个无参构造函数,函数体为空)
    class_name(){}
  2、拷贝构造函数。参数为const class_name& 的构造函数 (当类中没有定义任何拷贝构造函数时,编译器为默认提供一个拷贝构造函数,其功能为进行成员变量的赋值。)
    例如:定义一个对象的时候使用另外一个对象对其进行初始化。
    class_name class1;
    class_name class2=class1;或者(class_name class2(class1);)
    通过以上使用就需要用到拷贝构造函数,编译器默认的拷贝构造函数保证的是两个对象的物理状态相同(浅拷贝)。也就是说这是一种 浅拷贝。那么有浅拷贝就必然有深拷贝(其作用是保证两个对象在逻辑状态上相同)。
  例如代码:  
#include <stdio.h>

class Test
{
private:
int i;
int j;
public :
int get_i(void) {return i;}
int get_j(void) {return j;} }; int main(int argc, char *argv[])
{
Test Ta;
Test Tb(Ta);
printf("Ta.i = %d\t",Ta.get_i());
printf("Ta.j = %d\n",Ta.get_j());
printf("Tb.i = %d\t",Tb.get_i());
printf("Tb.j = %d\n",Tb.get_j());
return ;
}

运行结果:  

Ta.i = -    Ta.j =
Tb.i = - Tb.j =

  从运行结果中可以看出,对象Tb 与对象Ta中的变量i,j值完全相同。

修改代码:添加拷贝构造函数。

  

#include <stdio.h>

class Test
{
private:
int i;
int j;
public :
int get_i(void) {return i;}
int get_j(void) {return j;}
Test(){};
Test(const Test& t)
{
i = t.i;
j = t.j;
}
}; int main(int argc, char *argv[])
{
Test Ta;
Test Tb(Ta);
printf("Ta.i = %d\t",Ta.get_i());
printf("Ta.j = %d\n",Ta.get_j());
printf("Tb.i = %d\t",Tb.get_i());
printf("Tb.j = %d\n",Tb.get_j());
return ;
}

运行结果:从结果中可以看出结果同上面没有加入拷贝构造函数时一致。也就是说编译器给我们默认构造了一个拷贝构造函数。内容与下面代码一致  

Test(const Test& t)
{
i = t.i;
j = t.j;
}
Ta.i =     Ta.j =
Tb.i = Tb.j =

其中类中成员没有指代系统中的资源。所以看起来没有什么问题。

  修改代码:增加int *p = new int;并打印出p的地址

  

#include <stdio.h>

class Test
{
private:
int i;
int j;
int *p ;
public :
int get_i(void) {return i;}
int get_j(void) {return j;}
int* get_p(void){return p;}
void free(void){delete p;}
Test(int v)
{
i=;
j =;
p = new int;
*p = v;
};
Test(const Test& t)
{
i = t.i;
j = t.j;
p = new int;
*p = *t.p;
}
}; int main(int argc, char *argv[])
{
Test Ta();
Test Tb(Ta);
printf("Ta.i = %d\t,Ta.j = %d\t,Ta.p = %p\n",Ta.get_i(),Ta.get_j(),Ta.get_p());
printf("Tb.i = %d\t,Tb.j = %d\t,Tb.p = %p\n",Tb.get_i(),Tb.get_j(),Tb.get_p());
Ta.free();
Tb.free();
return ;
}

运行结果:  

Ta.i =     ,Ta.j =     ,Ta.p = 0x55d66fe34e70
Tb.i = ,Tb.j = ,Tb.p = 0x55d66fe34e90

如果在拷贝构造函数中去掉  p = new int;   *p = *t.p;

  运行结果:  

Ta.i =     ,Ta.j =     ,Ta.p = 0x55dbf6d60e70
Tb.i = ,Tb.j = ,Tb.p = (nil)

申请的 p 指针为空。这显然是不对的。

  打印p所指向空间的值运行结果:  

Ta.i =     ,Ta.j =     ,Ta.p = 0x563d1529ee70    ,*Ta.p=
Tb.i = ,Tb.j = ,Tb.p = 0x563d1377d9fd ,*Ta.p=
munmap_chunk(): invalid pointer
Aborted (core dumped)

  指针在释放的过程中出现了错误。

在拷贝构造函数中 增加 p = new int;   *p = *t.p;

  运行结果:  

Ta.i =     ,Ta.j =     ,Ta.p = 0x55b993806e70    ,*Ta.p=
Tb.i = ,Tb.j = ,Tb.p = 0x55b993806e90 ,*Ta.p=

关于深拷贝的说明:  ——自定义拷贝函数,必然需要使用到深拷贝

  到底什么时候需要用到深拷贝?    ——对象中有成员指代了系统资源。

  1、成员指向了动态内存空间。

  2、成员打开了外部文件。

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

我们上面的实验使用到了动态内存空间。所以也会出现问题。需要给它加上自定义拷贝函数。
修改代码如下:
intArray.cpp 
intArray::intArray (const intArray& obj)
{
Parray = new int[obj.arrayLenght];
arrayLenght = obj.arrayLenght;
for(int i=;i<obj.arrayLenght;i++)
Parray[i] = obj.Parray[i];
}

main.cpp

  

#include <stdio.h>
#include "intArray.h" int main(int argc, char *argv[])
{
int temp ;
intArray TestArray();
for(int i=; i<TestArray.getLenght();i++)
TestArray.changeArray(i,i);
for(int i=; i<TestArray.getLenght();i++)
{
if(TestArray.getArrayData(i,temp))
printf("getArrayData(%d) = %d\n",i,temp);
}
intArray TestArray1(TestArray);
for(int i=; i<TestArray1.getLenght();i++)
TestArray1.changeArray(i,i);
for(int i=; i<TestArray1.getLenght();i++)
{
if(TestArray1.getArrayData(i,temp))
printf("getArrayData1(%d) = %d\n",i,temp);
}
if(TestArray.getArrayData(,temp))
printf("getArrayData(%d) = %d\n",,temp);
TestArray.free();
TestArray1.free();
return ;
}

  运行结果:

  

getArrayData() =
getArrayData() =
getArrayData() =
getArrayData() =
getArrayData() =
getArrayData() =
getArrayData1() =
getArrayData1() =
getArrayData1() =
getArrayData1() =
getArrayData1() =
getArrayData1() =
  
 

C++ 对象的构造的更多相关文章

  1. android NDK 实用学习(三)- java端类对象的构造及使用

    1,读此文章前我假设你已经读过: android NDK 实用学习-获取java端类及其类变量 android NDK 实用学习-java端对象成员赋值和获取对象成员值 2,java端类对象的构造: ...

  2. .ctor,.cctor 以及 对象的构造过程

    摘要: .ctor,.cctor 以及 对象的构造过程.ctor:简述:构造函数,在类被实例化时,它会被自动调用.当C#的类被编译后,在IL代码中会出现一个名为.ctor的方法,它就是我们的构造函数, ...

  3. C++类继承中,基类/当前对象属性/当前对象的构造顺序

    [1]中提到,规范的派生类构造函数三个要点: 首先创建基类对象 应通过成员初始化列表,创建基类对象 应该初始化本派生类新增的成员变量 那在构造派生类实例的过程中,其基类(以及多继承的时候多个基类)/当 ...

  4. STL—对象的构造与析构

    STL内存空间的配置/释放与对象内容的构造/析构,是分开进行的.   对象的构造.析构         对象的构造由construct函数完成,该函数内部调用定位new运算符,在指定的内存位置构造对象 ...

  5. C++深度解析教程学习笔记(6)对象的构造和销毁

    1. 对象的初始化 (1)从程序设计的角度看,对象只是变量,因此: ①在栈上创建对象时,成员变量初始化为随机值 ②在堆上创建对象时,成员变量初始化为随机值 ③在静态存储区创建对象时,成员变量初始化为 ...

  6. C++中对象的构造顺序

    1,C++ 中的类可以定义多个对象,那么对象构造顺序是怎样的? 1,很多的 bug 是由对象的构造顺序造成的,虽然它不难: 2,对象的构造往往和构造函数牵涉在一起,构造函数的函数体又可能由非常复杂的程 ...

  7. c++中对象的构造和销毁

    对象的初始化 如下 ckasss Person { public: ]; char sex; int age; }; Person p={}; //对象初始化 构造数组对象时,需要一个没有参数的构造函 ...

  8. 【C++】类和对象(构造与析构)

    类 类是一种抽象和封装机制,描述一组具有相同属性和行为的对象,是代码复用的基本单位. 类成员的访问权限 面向对象关键特性之一就是隐藏数据,采用机制就是设置类成员的访问控制权限.类成员有3种访问权限: ...

  9. json对象、构造原型、组合继承

    一.json对象套路 var stu = { "name": "龙姑娘", age: 16, classmate: { name: "李小玉" ...

  10. C++程序设计方法3:派生类对象的构造和析构过程

    基类中的数据成员,通过继承成为派生类对象的一部分,需要在构造派生类对象的过程中调用基类构造函数来正确初始化: 若没有显示调用,则编译器会自动生成一个对基类的默认构造函数的调用. 若想要显示调用,则只能 ...

随机推荐

  1. 线程系列3--Java线程同步通信技术

    上一篇文章我们讲解了线程间的互斥技术,使用关键字synchronize来实现线程间的互斥技术.根据不同的业务情况,我们可以选择某一种互斥的方法来实现线程间的互斥调用.例如:自定义对象实现互斥(sync ...

  2. CAD二次开发中各类多段线的dxf组码

    Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; ed.WriteMessag ...

  3. -moz-box-shadow

    css的box-shadow是用来添加边框阴影效果的. 属性值详解: 1.inset可选值,默认阴影在盒子外使用inset后,阴影在盒子内,即使指定边框或者透明边框,阴影依然存在. 2.<off ...

  4. 安装Redis-cluster-gem install redis报错的解决方案

    错误描述: [root@eshop-cache01 local]# gem install redis ERROR: Loading command: install (LoadError) cann ...

  5. k8s1.11.0安装、一个master、一个node、查看node名称是ip、node是扩容进来的、带cadvisor监控服务

    一个master.一个node.查看node节点是ip # 安装顺序:先在test1 上安装完必要组件后,就开始在 test2 上单独安装node组件,实现node功能,再返回来配置test1加入集群 ...

  6. 【转】Apache HBase 问题排查思路

    [From]https://www.itcodemonkey.com/article/9426.html HBCK - HBCK检查什么? (1)HBase Region一致性 集群中所有region ...

  7. JavaScript参考DOM部分

    目录 DOM完整版 DOM 介绍 节点 节点树 Node接口 属性 方法 NodeList 接口,HTMLCollection 接口 介绍 NodeList.prototype.length Node ...

  8. IntelliJ IDEA 2018 for Mac使用技巧

    IntelliJ IDEA 2018 for Mac是一个综合性的Java编程环境,被许多开发人员和行业专家誉为市场上最好的IDE,它提供了一系列最实用的的工具组合:智能编码辅助和自动控制,支持J2E ...

  9. Linux C/C++基础——Windows远程登录Linux

    首先介绍两个ubuntu系统管理命令,用来测试连通性,及获取IP地址. 1.ping ping命令用来测试远程主机的连通性 使用方法:ping [参数] 远程主机IP地址 参数 功能 -a 每次相应时 ...

  10. 旗舰版win7系统中GraphEdit执行Loading a Graph From an External Process失败对策

    操作系统:旗舰版win7 DirectShow SDK: 9.0 IDE环境:VS2008 以下代码参考MSDN: HRESULT AddToRot(IUnknown *pUnkGraph, DWOR ...