CPP学习备忘[7] 对象生灭

本文深入探讨了C++中对象的构造、赋值操作及其生命周期管理,包括构造函数的作用、参数传递方式、无初始化对象定义、局部和静态对象的创建顺序、构造函数与析构函数的区别、拷贝构造函数与赋值操作符的实现及使用场景。重点阐述了如何在C++中有效管理对象的生命周期,确保程序的稳定性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【1】当以无初始化的形式,如:Date d;定义时,若创建全局对象,则以全0位的模式表示对象。若创建局部对象,则以随机值表示对象。

    作为对象的构造函数,其根本的使命就是创建对象实体,如果创建失败,这时若真要论及处理,就该让程序捕捉该异常,或者干脆终止程序的运行。因此构造函数的工作不以对象体作为返回值,也不以运行的成败状态作为继续运行的依据,构造函数的成功运行,确立了对象实体今后的操作合法性,构造函数的失败运行,预示着后继工作无法展开而必须另寻其他途径。

    构造函数是类型名称引导的定义语句,不是无返回值的函数调用语句,所以,构造函数也不是无类型函数。

    创建对象如果不给出对象名,也就是说,直接以类名调用构造函数,则产生一个无名对象,无名对象经常在参数传递时用到,如:cout<<Data(2003,11,11);

    C++语言在设计时,为了区别无初始化的对象定义和返回类对象的无参数函数声明的差别,也为了保持无初始化变量定义与无初始化(无参)对象定义的一致性,规定无参对象定义语句为:

    int a; //变量定义

    int b();    //返回整型值的无参函数声明

    Date g; //无参对象定义

    Data f();   //返回类对象的无参函数声明

的形式。

 

【2】在构造函数的参数列表右括号后面,花括号前面,可以用冒号引出构造函数的调用表,该调用表可以省略类型名称,但却行创建对象之职:

    class Student{

        string name;

        StudentID id;

     public:

        Student(string n=””no name”,int ssID):id(ssID),name(n){

            …

        }

    };

    其中写成id(ssID)的形式转而去调用StudentID的构造函数,目的是不要调用无参构造函数,而按构造函数参数列表中说明的参数要求去调用,因此它相当于调用:

    StudentID id(ssID);

引用只能在创建时进行变量实体的对应,对一个已经存在的引用来说,赋值语句并不表示再次与变量对应。也就是说在构造函数体中是不能完成对常量成员和引导成员的初始化的。

对于常量成员和引用成员也可以构造参数表的方式解决:

    class Silly{

        const int ten;

        int &ra;

     public:

        Silly(int x,int& a):ten(10),ra(a){}

    };

 

【2】局部和静态对象是指块作用域(局部作用域)和文件作用域的对象。它们声明的顺序与它们在程序中出现的顺序是一致的。而且静态对象只创建一次。

    和全局变量一样,所有全局对象在主函数(main)启动之前,全部已被构造。所以在程序启动之前已经有程序语句(构造函数)在那里被执行过了。因为调试总是从main函数的位置开始的,因此在开始捕捉错误之前,错误可能已经产生了。

    构造全局对象不像局部对象那么简单,全局对象没有明确的控制流来表明其顺序。因为全局对象还有多个源文件之间的协调问题,多文件的程序只有等到程序链接之后相互之间的关系才能搞定,但程序文件相互关系确定并不等于对象创建顺序确定,事实上,全局对象的创建顺序是编译器造出来的,因而不同的编译器做法就不同。为了避免不确定的问题,应尽量不要设置全局对象,这是程序设计重用及安全要诀,更不要让全局对象之间相互依赖。

    成员对象以其在类中声明的顺序构造。

    全局对象、常对象、静态对象都放在全局数据区的位置。要想让对象的声明期与运行的程序等寿命,只能放在全局数据区。

 

【4】默认拷贝构造仅仅拷贝了对象本体。

    自定义拷贝构造函数的参数必须是类对象的常量引用:

        Person(const Person& s);

    因为:

1.对象复制的语义本身尚处于当前定义当中,参数传递若为传值形式,则对象复制操作调用的拷贝构造函数在哪里?!所以只能是引用或者指针。

    2.但是指针参数将影响复制的语法:

        Person p2(*p1); //或者Person p1=*p2;这种语法并不优雅,所以用对象的引用;

    自定义拷贝构造函数在对象本体与对象实体不一致时,便是需要的,否则无此必要;

    当对象本体与对象实体一致时,其拷贝称为浅拷贝;当对象本体与对象实体不一致时,其拷贝称为深拷贝。深拷贝需要做动态内存分配的工作。因此,当对象的生命期终止时,也就是对象本体消失时,需要做好动态内存的释放工作。动态内存申请是人为的,因此释放也是人为的。

    因为析构函数没有参数,所以函数形式是唯一的,没有重载的析构函数。另外,析构函数的调用不是通过显式语句表示的,而是自动调用的。

 

【5】从fn(Student&)和Student(const string&)可以推得fn(string(“Jenny”))调用。这就是构造函数用来从一种类型转换为另一种类型的能力。不过须注意,推导过程是简单的,规则如下:

1.     只会尝试含有一个参数的构造函数;

2.     如果有二义性,则会放弃尝试;

3.     推导是一次性的,不允许多步推导;

 

【6】对象拷贝就是对象赋值。

    Person p1(“Ranny”);

    Person p2;

    p2=p1; //对象赋值

    类机制中有默认的赋值操作符,只要定义了类,就可以进行对象的赋值操作。但是默认的赋值操作符只管对象的本体的复制,如果对象之间要做深拷贝的话,则必须自定义赋值操作符。

自定义赋值操作符必须注意,原来的对象已经存在,要先将原来的资源释放掉,然后再进行深拷贝式的复制。

赋值操作的返回是引用返回,这是为了与赋值操作符的语义一致。因为赋值操作的结果是一个可以递进操作的左值。

    任何类,C++都有一个默认的赋值操作符,用来进行对象本体的复制。赋值操作符重载一般是在对象本体与对象实体不一致时,为了实现对象本体赋值操作以外的操作而进行的。重载了赋值操作符,默认的赋值操作符就不复存在。

    一般来说,赋值操作符是与拷贝构造函数和析构函数结队而行的。

    赋值操作必须判断对象是否在给自己赋值。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值