Item 4 :确定对象使用前已经被初始化

目录

一.构造函数

二.成员初值列表

三.不同编译单元的non-local static 对象的初始化



一.构造函数


在c和 c++中, 一些内置类型(其实也就是c++继承c的内容)是默认不被自动初始化的, 如int ,double 等等, 但在我的gcc 上, int 被默认初始化为0, char 被默认不输出,

double确显示来随机数。


所以,对于一些内置类型,我们保证不出错的最佳处理方法:永远在使用对象之前先将它初始化。


对于类这个我们自定义的类型, c++ 提供了多种函数来解决初始化的问题

默认构造函数, 拷贝构造函数,赋值运算符,这些都是为了避免对象未初始化而产生的一些问题。

看看区别

默认构造函数:当类没有定义任何构造函数的时候, 编译器将生成一个默认构造函数

{

        1.C++规定,每个类必须有默认的构造函数,没有构造函数就不能创建对象。

  2.若没有提供任何构造函数,那么c++提供自动提供一个默认的构造函数,该默认构造函数是一个没有参数的构造函数,它仅仅负责创建对象而不做任何赋值操作。

  3.只要类中提供了任意一个构造函数,那么c++就不在自动提供默认构造函数

}

拷贝构造函数:通过传递参数来构造一个对象。

赋值运算符:通过重载来实现对类的赋值 “ = ”。

来看一个具体的例子

#include <iostream>

using namespace std;

class Sales_item( )
{
    public:
        Sales_item( )
        {
                Isbn = " ";
                price = 0.0;
                num = 0;
                cout << "我是构造函数" << endl;
        }
        Sales_item(const Sales_item &obj)
        {
                Isbn = obj.Isbn;
                price = obj.price;
                num = obj.num;
                cout << "我是赋值构造函数" << endl;
        }
        Sales_item& operator= (const Sales_item &item)
         {
                 Isbn = item.Isbn;
                 price = item.price;
                 num = item.num;
                 cout << "我是 operator= 赋值操作符" << endl;
         }

         private:
              string Isbn;
              double price;
              std::size_t num;
};

int main( )
{
        Sales_item item1;
        Sales_item item2 = item1;
        Sales_item item3;
         
        item3 = item1;

        return 0;
}

运行结果:


通过上面可以分清了各种构造函数和它们什么时候调用


二.成员初值列表


但是上面所写构造函数不是最佳的做法

首先分清赋值和初始化, 上面所写的构造函数是通过赋值。初始化的时间发生的更早, 发生于这些 “成员” 的默认构造函数被自动调用之时,

比如说例子中类的

string Isbn;
这个成员在构造函数中不是被初始化,而是被赋值为"  " , 赋值前, 它会先调用自己的构造函数来初始化string 这个类型,效率会较低。


效率较高的做法是使用初始化列表

Sales_item( ): Isbn(" "), price(0), num(0) { }
Sales_item( const string &isbn, const double pri, const int nu): Isbn(isbn), price(pri), num(nu) { }
成员初始化列表避免了上面先调用默认构造函数为string赋值,然后在用构造函数赋值低效率的做法。

但对于内置类型int ,double初始化和赋值成本相同。


如果想使用默认构造函数来初始化成员

Sales_item( ):isbn( ) { }
这样isbn就会调用默认构造函数


使用成员初值列表另外一个好处是当我们的成员有const 和 引用时, 他们是不能被赋值的。



三.不同编译单元的non-local static 对象的初始化


“c++ 对定义于不同编译单元的non-local static对象的初始化次序并无明确定义”

分析这句话

编译单元:产出单一目标文件的源码。(单一源码文件加上头文件)

non-local static对象:先说静态对象包括全局对象, 命名空间对象和作用域对象, 以及在类,函数,文件中static修饰的对象。

在函数中的静态对象称为局部静态对象,其他称为非局部静态对象。


比如第二个文件中对象初始化会调用第一个文件的全局成员对象, 那么在第二个文件初始化完之前第一个文件被用的对象是否

被初始化了是个问题。我们不能保证它们的初始化次序。


解决方案:(单线程,但是在多线程中要注意,会有问题)

将每个non-local static 对象搬到自己的专属函数内,让这些函数返回一个引用指向 它所含的对象, 然后用户调用这些函数不直接

涉及这些对象,non-local static 对象被local static对象替换了.

class FileSystem { ... };
FileSystem& tfs( )//调用频繁可以设置为inline
{
       static FileSystem fs;
       return fs;
}
替换
extern FileSystem tfs;


原因:函数内的local static对象会在函数调用时被初始化。


总结:

1.了解各种构造函数

2.内置类型进行手工初始化。

3.构造函数最好使用成员初值列表, 而不要在构造函数本体内使用赋值操作。初值列列出的成员变量的次序应该和class中声明次序相同。

4。为避免“跨编译单元初始化次序”问题, 用local static对象替换non-local static对象



在补充一下深拷贝和浅拷贝(来源百度)

拷贝有两种:深拷贝,浅拷贝
当出现类的等号赋值时,会调用拷贝函数
在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。
但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。
所以,这时,必须采用深拷贝。
深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。
简而言之,当数据成员中有指针时,必须要用深拷贝。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天的技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值