C++学习笔记(九):类的构造函数

构造函数在C++中是特殊成员函数,用于确保每个对象的数据成员具有合适的初始值。文章详细解释了构造函数的定义、重载、初始化、初始化列表及特性,并通过实例展示了构造函数在实际编程中的应用。

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

  构造函数 是特殊的成员函数, 只要创建类类型的新对象, 都要执行构造函数。 构造函数的工作是保证每个对象的数据成员具有合适的初始值。  
   如下面:
      class Sales_item {
           public: 
            sales_iterm(): units_sold(0), revenue(0.0) {}
     };

   构造函数的一些特性
    和类的名字一样,没有返回值, 可以有形参,也可以没有形参
 
    构造函数的重载
     但有个问题是,我们如何知道应该定义哪个或多少个构造函数?  
     根据需要制定的方式来构造函数,必要时可以添加
    
     系统根据实参来决定使用哪个构造函数。  
 
    构造函数自动执行
     只要创建该类型的一个对象, 编译器就运行一个构造函数。
   
    构造函数不能声明为const, 如:
      class sales_item {
      public:
          Sales_item() const; // error
     };
     
    构造函数的初始化式
      与其它任何函数一样,构造函数具有名字、行参表和函数体。 但与其它函数不同的是, 构造函数也可以包含一个构造函数初始化列表:
        Sales_item :: Sales_item (const string &book): isbn(book), units_sold(0), revenue(0.0) {}

    构造函数可以定义在类的内部或外部, 构造函数初始化式只在构造函数的定义中而不是声明中指定。

    一些隐式初始化
    Sales_item::Sales_item(const string &book)
    {
        isbn = book;
        units_sold = 0;
        revenue = 0.0;
    }
      从概念 上讲,  可以认为构造函数分两个阶段执行:  1  初始化阶段, 2 普通的计算阶段, 计算阶段由构造函数体中的所有语句组成
  
      在构造函数初始化列表中没有显示提及的每个成员, 使用与初始化变量相同的规则来进行初始化:  内置或复合类型的成员的初始值依赖于对象的作用域: 在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为0
       
       有时需要构造函数初始化列表:
        有些成员必须在构造函数中进行初始化。 对于这样的成员,在构造函数函数体中对它们赋值不起作用。 其中包括:没有默认构造函数的类类型的成员,以及const或引用类型的成员

     如:  
     class ConstRef {
         public:
           ConstRef (int ii);
         private:
            int i;
            const int ci;
            int &ri; 
     };

    //  错误的赋值方法:
     ConstRef::ConstRef(int ii)
     {
          i = ii;
          ci = ii ; // 这里  ci是const类型,不能被赋值
           ri = i;   // 这里 ri是引用类型, 不属于该类对象
      }
      
    正确的赋值方法:
     ConstRef:: ConstRef(int ii) : i(ii), ci (ii), ri(i){}
   
      其实, 初始化和赋值 严格来讲都是低效率的;  数据成员可能已经被直接初始化了, 还要对它进行初始化和赋值。
      但是,某些类成员 必须得初始化 如const 或引用类型成员,以及 没有默认构造函数的类类型的任何成员

     初始化 最好不要用一个类成员来初始化另一个类成员
   
     初始化式可以是任意表达式
     如:  Sales_item (const std::string &book, int cnt, double price):
           isbn(book), units_sold(cnt), revenue(cnt * price) {}
          
   这里,由于string是一个类,因此 book是类类型成员,
    类类型的数据成员的初始化式:
      如: Sales_item (): isbn(10, '9'), units_sold(0), revenue(0,0){}

    默认的构造函数:
    1 合成的默认构造函数:
        当有类类型的成员变量时,就出现了合成的默认构造函数。  
       内置和复合类型的成员,如指针和数组, 只对定义在全局作用域中的对象才初始化, 也就是如果定义在main函数中的对象,就可以不初始化。 这是因为,定义在全局中的变量一般都是静态编译的,而不是动态执行的,它是需要存储在堆或者静态区的。因此,必须得初始化 。


    此外, 每个构造函数应该为每个内置或复合类型的成员提供初始化式,没有初始化式的成员处于未定义的状态。  除了作为赋值的目标之外,以任何方式使用一个未定义的成员都是错误的。如果每个成员的状态已知, 则可以区分空对象和具有实际值的对象。

    类通常应定义一个默认构造函数:
    如果一个类定义了构造函数,那么编译器将不合成默认构造函数。  NoDefault没有默认构造函数,意味着:
      1  必须通过定义的构造函数来显示的初始化NoDefault成员
      2  编译器不会为具有NoDefault类型成员的类合成默认构造函数。  如果这样的类希望提供默认构造函数,就必须显示地定义
      3 NoDefault类型不能用作动态分配数组的元素类型
      4 NoDefault 类型的静态分配数组必须为每个元素提供一个显示的初始化。
      5 不能与容器结合使用

  隐式类类型转换:
    这个是说,当我们调用某个类的函数时,我们向这个函数传递了一个参数,而该类的对象会首先调用它的构造函数,该构造函数有可能一句这个传递的参数来进行初始化对象, 而后,当该函数结束,时, 构造的该对象就不能再访问了,它是一个完全丢弃的对象

  1 抑制由构造函数定义的隐式转换:
     可以通过 将构造函数声明为explicit, 来防止在需要隐式转换的上下文中使用构造函数:
      class Sales_item {
       public:
            explicit Sales_item (const std::string &book = ""): isbn(book), units_sold(0), revenue(0.0) {}
            explicit Sales_item(std::istream &is);
      }

   explicit 关键字只能用于类内部的构造函数声明上。 

    定义了explicit的构造函数的类, 如果再直接没有初始化而给函数传递一个参数,可能就不行了
    
    2 为转换而显示地使用构造函数:
     string null_book = "9-99-99999-9";
      item.same_isbn(Sales_item(null_book);  // 那就只能通过这种方式 来给函数传递参数了。

  类成员的显示初始化:
    对于没有定义构造函数并且其全体数据成员 均为public类, 可以采用如下方式:
    结构体的初始化:
     struct Data {
        int ival; 
        char *ptr;
     };
 
    都初始化为0
    Data vall = {0 , 0};

    但是, 显示初始化类类型对象的成员有三个重大的缺点:
    1 要求类的全体数据成员都是public
    2 程序员的工作加重了
    3 如果增加和删除一个成员,那么所有初始化的对象都要更新。
  
   最佳方式,要定义一个默认构造函数, 允许编译器自动运行那个构造函数。 以保证每个类对象在初次使用之前正确地初始化


   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值