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

  例如:  

#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. 伪分布式下Hadoop3.2版本打不开localhost:50070,可以打开localhost:8088

    一.问题描述 伪分布式下Hadoop3.2版本打不开localhost:50070,可以打开localhost:8088 二.解决办法 Hadoop3.2版本namenode的默认端口配置已经更改为9 ...

  2. JDBC——JDBC基础

    1.JDBC与数据库的交互过程概括性来说,JDBC与数据库交互有以下这些步骤:1.建立一个到数据库的连接.2.在数据库中对表执行检索.创建,或修改的SQL查询.3.关闭到数据库的连接.JDBC的类和接 ...

  3. CentOS7 yum install elasticsearch

    首先安装 JDK 环境 # 本机是否已经安装,ElasticSearch 最低支持 jdk 1.7 yum list installed | grep java # 查看 yum 库中的 java 安 ...

  4. Hibernate系列1:入门程序

    1.传统的java数据库连接 在传统的开发中,如果要建立java程序和数据库的连接,通常采用JDBC或者Apache Commons DbUtils开发包来完成.他们分别有以下特点: JDBC: 优点 ...

  5. js设计模式-代理模式

    1.什么是设计模式? 设计模式:在软件设计过程中常用的代码规范,针对特定的场景 2.应用场景: 麦当劳点餐  观察者模式   规定的代码格式 花店送花  :代理模式 真实对象(男同学)-----代理对 ...

  6. 域名到IP 报错socket.gaierror: [Errno 8] nodename nor servname provided, or not known

    Python中如何通过域名,查看对应的IP? 请看如下代码: import socket hostname="www.baidu.com" ip = socket.gethostb ...

  7. PreparedStatement执行sql語句

    import com.loaderman.util.JdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; i ...

  8. 阶段3 3.SpringMVC·_05.文件上传_1 文件上传之上传原理分析和搭建环境

    分成几个部分 里面可能就包含文件上传的值 提交方式要改成post 第三个就是提供一个input file的文件选择域 新建项目 新建一个项目 当前项目没有父工程 跳过联网下载 改成02 构建 编译和目 ...

  9. 集群中配置多台机器之间 SSH 免密码登录

    集群中配置多台机器之间 SSH 免密码登录 问题描述 由于现在项目大多数由传统的单台机器部署,慢慢转变成多机器的集群化部署. 但是,这就涉及到机器间的 SSH 免密码互通问题. 当集群机器比较多的时候 ...

  10. Java语言实现 Base64 加密 & 解密

    Java语言实现 Base64 加密 & 解密 Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法. Base64 ...