谈谈c++的初始化工作(2)

原创 2003年05月26日 15:32:00


我们首先来看上次遗留的问题。
把(1)中的代码换为注释部分,或许您一时还认识不到会有什么发生,但最终是通不过的,调试抛出异常,信息如下:
未处理的“System.Runtime.InteropServices.SEHException”类型的异常出现在 TestInit.exe 中

其他信息:外部组件发生异常。

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Press any key to continue

我想,您回头再细看的话,就会明白为什么如此了(我们写程序一定要追问到底:)。

我们今天要谈的是,一些变量只有唯一的初始化形式,通过例子,告诉您要特别注意。然后,我们就一步一步,来看资源浅拷贝的问题。我相信初学c++的同学,会对“拷贝函数”有些疑问,它就是为了解决上述问题的;但事实上,还有一个隐藏的地方,今天我也想给您指出。
这些程序,可是我特意设计的哦。希望可以很方便的认识问题所在,与解决之道。

首先,看第一个例子。在类中,这两类变量:
e.g.
Name &name;    //引用
const int ID;  //常量
它们的初始化形式是唯一的。而且必须由您来初始化。
看下面的程序:
 
//human.h
#pragma once

class Name
{
    char *name;
public:
   //...
};
class Human
{
 Name &name;
 const int ID;//每个人都唯一的标志号
 //... 
public:
 Human(void);
 ~Human(void);
 
 //...
};
//human.cpp
#include "human.h"
#using <mscorlib.dll>

//默认的构造函数
Human::Human(void)
{
}

Human::~Human(void)
{
}

写一个主文件测试。
但调试出错,错误信息文件为:
/*----------------------------------------------------------------------------
//Human:error file
------ 已启动生成:项目:TestInit, 配置:Debug Win32 ------

正在编译...
Human.cpp
Human.cpp(5) : error C2758: “Human::name” : 必须在构造函数基/成员初始值设定项列表中初始化
        e:/NET/Small_code/TestInit/Human.h(13) : 参见“Human::name”的声明
Human.cpp(5) : error C2758: “Human::ID” : 必须在构造函数基/成员初始值设定项列表中初始化
        e:/NET/Small_code/TestInit/Human.h(14) : 参见“Human::ID”的声明
fmain.cpp
Date.cpp
正在生成代码...

生成日志保存在“file://e:/NET/Small_code/TestInit/Debug/BuildLog.htm”中
TestInit - 2 错误,0 警告


---------------------- 完成 ---------------------

    生成:0 已成功, 1 已失败, 0 已跳过
--------------------------------------------------------------------------------
*/
   
    因为这里涉及的是仅仅的c++语法,我就不多费口舌了,如何改正,希望您能动手试试,一定要动手,不要想当然哦~~~
    当然,如果您是爱问题的人,我想您可以这样深究一下:设计c++语言时,为什么诸如int类型成员变量能提供默认初始化,而它们却不能;从编译角度,刻意给它们提供如int类型般的初始化会有什么困难和问题?


   下面详细谈什么是资源浅拷贝问题。沿袭c的习惯,c++对系统自分配的资源进行统一管理,但是,用户申请的资源,则有用户来释放。
   比如:

       userType *p=new userType(/*---*/);
       //...
       delete p;
       //delete释放一般是不可忘的

    单独的变量或许对您来说是不成问题的。但在类中,这些情况就变的相当复杂。处理不好,您的系统要么就是因为内存泄露而运行不下去,而要么就是异常频频发生。
    我们先来看一些c++的默认操作:
    //...
    class OneClass {
        int _value;
    public:   
        OneClass(int _val=0):_value(_val) {}
        ~OneClass() {}

        //...
    };

    //you may use in this way:
    OneClass oneObj(7);
    OneClass anotherObj;
    anotherObj=oneObj;//(1)
    //...
    //int Compare(OneClass one,OneClass two);
    int k=Compare(oneObj,anotherObj);//(2)
    //...

    在本程序的场景下,上面的代码是可以完好工作的,但您清楚(1)与(2)系统都作了什么了吗?您是否知道,如果您的初始化工作做的不好的话,即使,就沿用上面的初始化习惯,您的程序很容易就崩溃了呢。
    答案是,(1)语句执行时,默认的,系统试图把oneObj的资源全部copy给anotherObj,但用户申请的资源(new来的:),却传入的是地址;(2)语句的默认形参传递遵循同样的规则。
    当然这与java与c#是不同的,因为java与c#的对象是引用类型。而c++,除非您强行定义为引用类型的,它就不是。

    我们来看下面的例子,第一遍我建议您只看程序,不要往下看,看您能否发现什么问题。

//human.h   
 #pragma once

#define NULL 0

class Name
{
    char *name;
public:
 Name(char *_name=NULL):name(_name) {}
 ~Name() { }

 char *getName(){ return name;}

};
class Human
{
 Name *name;//
 int ID;    //唯一化标志
public:
 Human(int id=0,char *_name=NULL);
 ~Human(void);

 int getID()const { return ID;}
 Name *getName() { return name;}
};

//human.cpp
#include "human.h"
#using <mscorlib.dll>

Human::Human(int id,char *_name):ID(id)
{
   name=new Name(_name);//初始化:指针
}

Human::~Human(void)
{
 delete name;//析构时释放资源
}

//fmain.cpp
#include <iostream>
#include "human.h"

void main()
{      
       //测试程序
 try{
 Human lily(11100120,"lily");
 Human lucy=lily;
 }
 catch(...)
 {//如果有any异常
  std::cout<<"/n Unknown Exception.../n";
 }
 
}  

//请回头看程序,您觉得一切都好吗?


    事实上,调试过程中,等三个异常忽略后,就会得到下面的结果:
/*   
After three exceptions occured you get :

 Unknown Exception...
Press any key to continue

*/
    为什么?
    看这几行代码:
        Human lily(11100120,"lily");
 Human lucy=lily;
    虽然一开始,我就给了小例子,形式一样,那个没问题。何以这个就不行了呢?因为类的定义不同。
    由c++工作的机理,这行
       Human lucy=lily;
    是把lily的资源拷贝给lucy(lucy是不调用构造函数的),可是,因为其中的name是用户申请的资源,并不能把它也拷贝过去,而是直接传了地址。这样,您知道吗,lucy.name和lily.name的地址是一样的。
    于是,当一个的析构函数调用后,name所指向的资源已被释放掉了的。而另外一个类的析构函数又去释放,问题来了---程序崩溃了!
   
    这就是浅拷贝问题---“浅”的不完全的拷贝:)。
   
    找到原因,我们就办法。解决的办法是,这份工作自己来做!
    写一个拷贝赋值操作(public):

    形式为:   className &operator=(className &obj){ /*...*/}
    您看下面的解决办法和结果:
/*

If you add ...in class Human(public):

 Human &operator=(Human &human){
  if(this!=&human){
      ID=human.getID();
      name=new Name(human.getName()->getName());
      return *this;
  }
 }
 
 
OK,and you get:

Press any key to continue

That is what we want!
*/

    下面的例子,是拷贝函数的问题,在上面的基础上,我改动了一下程序的结构,
定义了一个名空间。
    具体的问题分析我留给下次,您就有机会细细的看了。您是否一切都清楚了?
您可以解决这个问题吗?

//human.h
#pragma once
#using <mscorlib.dll>
namespace Humanbeing
{
#define NULL 0

class Name
{
    char *name;
public:
 Name(char *_name=NULL):name(_name) {}
 ~Name() { }

 char *getName(){ return name;}

};
class Human
{
 Name *name; //
 int ID;     //唯一的标志
public:
 Human(int id=0,char *_name=NULL):ID(id)
 {
  name=new Name(_name);//申请资源
 }
 ~Human(void)
 {
  delete name;//释放资源
 }

        //拷贝赋值操作
 Human &operator=(Human &human){
  if(this!=&human){
      ID=human.getID();
      name=new Name(human.getName()->getName());
      return *this;
  }
 }
 
 int getID()const { return ID;}
 Name *getName() { return name;}
};

//名空间里的函数
bool IsSameMan(Human one,Human another)
{
    if(one.getID()==another.getID())
  return true;
 else return false;
}

}

//测试文件
#include <iostream>
#include "human.h"
void main()
{
 using namespace Humanbeing;
 try{
 Human lily(11100120,"lily");
 Human lucy=lily;

 if(IsSameMan(lucy,lily))
 {
  std::cout<<"/n They are the same one./n";
 }else
  std::cout<<"/n No,they're not the same one./n";

 }
 catch(...)
 {
  std::cout<<"/n Unknown Exception.../n";
 }
 
}


     调试结果呢,是连续六个异常后,出现:
After six exceptions occured you get :

 They are the same one.

 Unknown Exception...
Press any key to continue

     为什么(上次异常是三个,这次是六个,可以解释吗)?怎么办?  

谈谈对工作的看法

好的团队能让你知道如何合作,如何沟通甚至是一个好的工作方式与习惯,一个好的领导除了能给你职业方面的培训,还会交给你如何形成自己的职业观、价值观,一群好的同事能让你不断地进步、快速的成长。一个好的行业会...
  • linux_zkf
  • linux_zkf
  • 2015年08月26日 08:44
  • 4231

谈谈c++的初始化工作(4)

      按照惯例,我们简单回顾一下上次的问题。上次最后说,“ 所有的这些问题,在组合的情况下,也会发生吗?在对象数组初始化时,如果我们希望每个对象的初始化状态都不同,应该如何做?”,关于组合的情况...
  • PercyLee
  • PercyLee
  • 2003年06月10日 00:12
  • 964

谈谈c++的初始化工作(1)

       c++博大精深,这是很多了解c++的人的感觉。越是深入,越是觉得她会给你很好的训练---让你成为真正的程序设计者。     我想从她的初始化工作着手,试图展现其一角,希望能有助于您提高学...
  • PercyLee
  • PercyLee
  • 2003年05月26日 13:37
  • 1149

谈谈c++的初始化工作(3)

       我们还是先来看看上次遗留的问题。“为什么(上次异常是三个,这次是六个,可以解释吗)?怎么办?”这其中的原因,我想您是明白的,我只做简单的重复:)。代码段中:    //bool IsSa...
  • PercyLee
  • PercyLee
  • 2003年06月01日 23:01
  • 1440

MapReduce详细的工作流程(MapReduce2)

上一篇详细讲了MapReduce1的工作流程,这一篇主要讲基于YARN系统的MapReduce 2的工作流程。 对于大于4000个节点的集群来说,MapReduce1系统将会产生一个规模瓶颈,因此Y...
  • guoyuguang0
  • guoyuguang0
  • 2015年07月03日 15:42
  • 337

工作10多年的感想

如今,在it行业摸爬滚打10多年了。 从传统行业软件  到   互联网的软件, 从互联网 到 移动互联网; 从移动互联网 到 大数据,云计算; 现在 还得学习AI。 一刻都没停息过,难道真的要活到老,...
  • RSS_40728440
  • RSS_40728440
  • 2018年01月12日 16:42
  • 81

谈谈企业的数据工作!——企业的数据分析能力金字塔

写在前面 笔者写这篇文章的初衷源于两个故事: 故事一:一位在互联网行业做数据库架构多年的同事一起吃饭,问起我现在在说什么,我说自己在做医疗方面数据分析,同事笑,说:你有很多资源啊,只要你能拿到电子...
  • hualalalalali
  • hualalalalali
  • 2017年05月10日 15:34
  • 1172

谈谈架构师的职责(一)

很早就想写一篇文章来谈谈架构师的职责了,因为自己做架构设计也有几年了,有得有失,想以此文来谈谈自己对架构师职责的认识。架构师这个话题很大,在这里不打算深入详谈,只是简要的谈谈,想到哪里说到哪里。在谈架...
  • u013301846
  • u013301846
  • 2014年05月01日 02:07
  • 575

c++的初始化与清除

第4章 初始化与清除 为什么c++中要有构造和析构函数呢?
  • luojj26
  • luojj26
  • 2016年03月02日 19:44
  • 814

架构师的工作是什么?

仅仅做出决策是不够的,我们可以从时间线上来观察:在做出决策之前,架构师需要足够了解自己的“可选项”,无论是用户的实际需求,还是最新出现的技术和框架,并且都得要有足够深入的理解(否则就是在拍脑袋做决策)...
  • lz0426001
  • lz0426001
  • 2015年10月26日 21:02
  • 462
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:谈谈c++的初始化工作(2)
举报原因:
原因补充:

(最多只允许输入30个字)