zoukankan      html  css  js  c++  java
  • C++

    explicit构造函数

    考虑一个类Date:

    class Date{
      int d,m,y;
      //...
    };
    
    void my_fact(Date d);
    
    void f()
    {
      Date d{15};  //似乎合理:x变为{15,today.m,today.y}
      d = 15;    //含混
      my_fact(15);  //含混
      //...
    }    

    这最多是一段含混代码,数据15和Date之间并没有清晰的逻辑关联。

        但是,我们可以指明构造函数不能用作隐式类型转换。如果构造函数的声明带有关键字explicit,它只能用于初始化和显示类型转换。例如:

    class Date{
      int d, m, y;
    public:
      explicit Date(int dd = 0,int mm=0,int yy = 0);
      //...
    };
    
    Date d1{15};    //OK:被看作显式类型转换
    Dated2 = Date{15};    //OK:显式类型转换
    Date d3 = {15};    //错误: = 方式的初始化不能进行隐式类型转换
    Date d4 = 15;    //错误: = 方式的初始化不能进行隐式类型转换
    
    void f()
    {
      my_fact(15);    //错误:参数传递不能进行隐式类型转换
      my_fact({15});  //错误:参数传递不能进行隐式类型转换
      my_fact(Date{15});  //OK:显式类型转换
    }

    用 = 进行初始化可以看做 拷贝初始化,一般而言,初始化器的副本会被方式待初始化的对象。但是,如果初始化器是一个右值,这种拷贝可能被优化掉(取消),而采用移动操作,省略 = 会将初始化变为显式初始化。显式初始化也称为 直接初始化。

      默认情况下,应该将单参数的构造函数生命为explicit。除非有更好的理由,否则应该按照这种默认的方式去做。如果定义隐士构造函数,最好写下原因,否则代码的维护者可能怀疑你疏忽了,或者不懂这一原则。

      如果一个构造函数声明为explicit且定义在类外,则在定义中不能重复explicit:

    class Date{
      int d,m,y;
    public:
      explicit Date(int dd);
    //....
    };
    
    Date::Date(int dd){/*...*/}    //OK
    explicit Date::Date(int dd){/*...*/}  //错误

    大多数explicit起重要作用的构造函数都接收单一参数。但是,explicit也可以用于五参或多个参数的构造函数。例如:

    struct X{
      explicit X();
      explicit X(int, int);
    };
    
    X x1 = {};    //错误:隐式的
    X x2 = {1,2};    //错误:隐式的
    
    X x3{};          //OK:显式的
    X x4{1,2};    //OK:显式的
    
    int f(X);
    int i1 = f({});    //错误:隐式的
    int i2 = f({1,2});     //错误:隐式的
    
    int i3 = f(X{});    //OK:显式的
    int i4 = f(X{1,2});  //OK:显式的

    More:参考链接

    mutable:

    在const对象中,一个定义为mutable的成员可以被修改:

    class Date{
    public:
        //.....
        string string_rep() const;
    private:
        mutable bool cache_valid;
        mutable string cache;
        void compute_cache_value() const;
        //..... 
    };
    
    string Date::Date string_rep() const
    {
        if(cache_valid){
            compute_cache_value();
            cache_valid = true;    
        }
        return cache;
    }

    此种情况下,string_rep()即可用于const对象,也可用于非const对象。例如:

    void f(Date d,const Date cd)
    {
        string s1 = d.string_rep();
        string s2 = cd.string_rep();    //OK
    }

    this 自引用:

    定义状态更新函数ad_year(),add_month() 和 add_day()没有返回值。对这样一组相关的更新函数,通常有用的技术是令它返回已更新对象的引用。例如:

    void f(Date& d)
    {
      //...
      d.add_year(1).add_month(1).add_year(1);
      //...
    }
    //为此,必须将每个函数都声明为返回一个Date引用:
    class Date{
      //...
      Date& add_year(int n);  //增加n年
      Date& add_month(int n);  //增加n个月
      Date& add_day(int n);    //增加n天
    };

    每个(非static)成员函数都知道是哪个对象调用的它,并能显式引用这个对象。例如:

    Date& Date::add_year(int n)
    {
      if(d == 29 && m == 2 && !leapyear(y + n))  //小心2月29
      {
         d = 1;
         m = 3;
      }
      y += n;
      return *this;
    }

    表达式 *this 引用的就是调用此成员函数的对象。注意:this是一个右值,无法获得this的地址或对其赋值。this的使用大多是隐式的。this的显式应用一种体现是链表操作。例如:

    struct Link{
      Link* pre;
      Link* suc;
      int data;
    
      Link* insert(int x)    //在this之前插入x
      {
         return pre = new Link(pre,this,x);
      }  
       void remove()   //删除并销毁this
      {
          if(pre) pre->suc = suc;
          if(suc) suc->pre = pre;
          delete this;
      } 
    };

    另外,从一个派生类模板访问基类的成员也要显式使用this。

    显式初始化:

    C ++支持两种显式初始化形式。

    在括号中提供初始化程序列表:

    String sFileName(“FILE.DAT”);

    括号列表中的项目被认为是类构造函数的参数。这种初始化形式使得能够初始化具有多个值的对象,并且还可以与新的运算符一起使用。例如:

    Rect * pRect = new Rect(10152497);

    使用等号初始化语法提供单个初始化器。例如:

    String sFileName =“FILE.DAT”;
    • 尽管上述示例的工作方式与第一个列表项中的String所示示例相同,但该语法不适用于在自由存储上分配的对象。

      等号右侧的单个表达式作为类的复制构造函数的参数; 因此,它必须是可以转换为类类型的类型。

      请注意,由于初始化上下文中的等号(=)与赋值运算符不同,因此重载operator =对初始化没有影响。

    等号初始化语法与函数式语法不同,即使生成的代码在大多数情况下相同。不同的是,当使用等号语法时,编译器必须像以下事件序列一样运行:

    • 创建与正在初始化的对象相同类型的临时对象

    • 将临时对象复制到对象。

  • 相关阅读:
    BZOJ 1057 悬线法求最大01矩阵
    POJ 2248
    SPOJ
    51NOD
    2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest J. Judging the Trick
    POJ 1379 模拟退火
    POJ 2420 模拟退火
    Frontend 事后诸葛亮
    【Frontend】Alpha Review 展示博客
    ASE19 团队项目 alpha 阶段 Frontend 组 scrum5 记录
  • 原文地址:https://www.cnblogs.com/gardenofhu/p/7596830.html
Copyright © 2011-2022 走看看