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

  例如:  

#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. .net 查壳工具

    请问大神.NET查壳工具都有哪些? 已知的有DotNet Id    除了这个还有别的吗?脱MAXTOCODE发现是双壳.脱掉第一层还有一层,DotNet Id检测没壳了,但是反编译还是加密状态. 用 ...

  2. 开源运维自动化平台-opendevops

    开源运维自动化平台-opendevops 简介 官网 | Github|  在线体验 CODO是一款为用户提供企业多混合云.自动化运维.完全开源的云管理平台. CODO前端基于Vue iview开发. ...

  3. oracle中删除某个用户下的所有表

    一般的方法:先使用sql查询: SELECT 'DELETE FROM '|| table_name || ';' FROM USER_TABLES ORDER BY TABLE_NAME; 将查询结 ...

  4. Example Bookstore schema showing how data is sharded DATABASE SHARDING

    w公共查询表复制至每一个碎片 http://www.agildata.com/database-sharding/ In the Bookstore example, the Primary Shar ...

  5. LC 683. K Empty Slots 【lock,hard】

    There is a garden with N slots. In each slot, there is a flower. The N flowers will bloom one by one ...

  6. ASP.NET Core-Docs:在 ASP.NET Core 中启用跨域请求(CORS)

    ylbtech-ASP.NET Core-Docs:在 ASP.NET Core 中启用跨域请求(CORS) 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 1. ...

  7. 解决:Oracle 异常 ORA-01861: literal does not match format string(字符串格式不匹配)

    今天在写存储过程的时候,老是报一个错.如下,记录下来,以供日后查阅. 报错:  Oracle 异常 ORA-01861: literal does not match format string(字符 ...

  8. python 类中__call__内置函数的使用

    class F: def __call__(self, *args, **kwargs): print('执行__call__') s = F()s() 先给类创建一个对象,直接通过对象来执行,就会自 ...

  9. python-Web-django-短信登陆

    until: import json, urllib from urllib.parse import urlencode # 发送短信 def request2(mobile,num, m=&quo ...

  10. LeetCode.1103-向人们分发糖果(Distribute Candies to People)

    这是小川的第393次更新,第425篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第256题(顺位题号是1103).我们通过以下方式向一排n = num_people个人分 ...