V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zxCoder
V2EX  ›  C++

关于 C++ default constructor 的疑问

  •  
  •   zxCoder · 65 天前 · 1001 次点击
    这是一个创建于 65 天前的主题,其中的信息可能已经有所发展或是发生改变。

    看 Essential C++这本书

    构造器这块实在很难理解,也可能是其他语言先入为主导致的...

    书上说默认构造函数有两种形式,一种是无参,一种是给每一个参数提供默认值。

    如果我这两种形式都定义了,这时候Triangular t1;就会出现 ambiguous 的编译错误,是因为 Triangular(int len = 1, int bp = 1)这个构造函数也被认为是无参构造?

    但是我如果再定义一个Triangular(int len, int bp);也不行,这时候Triangular(int len = 1, int bp = 1)这个构造函数是被认为是两个参数的构造函数?

    然后如果我的构造函数定义在 class 外,变成Triangular::Triangular(int len, int bp){}Triangular::Triangular(){},这也完全看不出这是两个冲突的 default constructor 了。。

    这几个关系搞得有点乱,有大佬说说 C++这块的设计哲学吗。。。总感觉有点难记

    class Triangular {
    public:
        Triangular();
    //    Triangular(int len);
        Triangular(int len = 1, int bp = 1);
    //    Triangular(int len, int bp);
    
    private:
        int _length;
        int _beg_pos;
        int _next;
    };
    
    Triangular::Triangular(int len, int bp) {
        _length = len > 0 ? len : 1;
        _beg_pos = bp > 0 ? bp : 1;
        _next = _beg_pos - 1;
    }
    
    Triangular::Triangular() {
        _length = 1;
        _beg_pos = 1;
        _next = 0;
    }
    
    
    int main() {
    //    Triangular t1;                   // 无参构造
        Triangular t2 = 5;                 // 一个参数的构造函数
        Triangular t3(1);            // 一个参数的构造函数
        Triangular t4(1, 2);  // 两个参数的构造函数
    //    Triangular t5();                 // 为了兼容 C,这被认为是函数调用而不是无参构造
        return 0;
    }
    
    13 条回复    2021-03-11 22:19:41 +08:00
    hello2060
        1
    hello2060   65 天前 via iPhone   ❤️ 1
    已经忘了 c++了,随便猜猜

    第一种情况,一个无参数,一个默认参数,冲突是正常的啊。

    第二种情况,一个无参数,一个两个参数,第二个不是默认构造函数,也很正常啊
    hello2060
        2
    hello2060   65 天前 via iPhone
    手机上看贴的,似乎你说的是第二种情况还是有默认参数?
    anonymous256
        3
    anonymous256   65 天前
    “是因为 Triangular(int len = 1, int bp = 1)这个构造函数也被认为是无参构造?” 是啊
    你没提供参数,编译器不知道选择哪个构造函数了。 你需要移除一个构造函数
    hello2060
        4
    hello2060   65 天前
    你的 code 里这种写法和第一种是一样的吧,默认参数只要在声明里指定,定义里不用吧
    ipwx
        5
    ipwx   65 天前
    ... 为什么要用这种有歧义的语法。。。

    你难道会定义两个函数,一个 int f(); 一个 int f(int a=1, int b=2); 嘛?
    wutiantong
        6
    wutiantong   65 天前
    1. 你定义函数时指定了一些参数
    2. 你为函数参数配置了一些默认值
    3. 你写一句调用函数的表达式时,编译器尝试通过各种上下文信息来找到正确的被调用函数(静态重载)

    以上是三件独立的事情,不要混淆在一起理解。
    yulon
        7
    yulon   65 天前
    如果没有冲突的话,你觉得你要怎么调用,才能利用到 len = 1 这个默认值?
    GeruzoniAnsasu
        8
    GeruzoniAnsasu   65 天前
    首先像 #6 说的先把默认构造 / 构造函数带有默认参数 / 编译器选择重载这三者区分开来

    Class() 无参默认构造
    Class(type param=1) 带有一个参数的构造函数,且这个函数有默认值

    定义的时候,这是两个不同的构造函数,这应该不难理解



    然后如果一个类同时存在这两个构造函数,当你写下 Class obj 时,编译器会寻找可以满足对象定义语义的构造函数,于是这两个函数都能被找到,于是会产生 ambiguous 。当你写 Class obj(1) 的时候,无参构造不能满足语义,只有第二个构造可以满足,因此无歧义
    dangyuluo
        9
    dangyuluo   65 天前
    看编译器报错,找 candidate 关键词
    anonymous256
        10
    anonymous256   61 天前
    我重新回答一下你的问题,。简化一下你的代码,下面这样的写法中,`Triangular t1; ` 语句同样会报错。

    ```cpp
    class Triangular {
    public:
    Triangular();
    Triangular(int len = 1, int bp = 1);
    private:
    int _length;
    int _beg_pos;
    int _next;
    };

    Triangular::Triangular(int len, int bp) {
    _length = len > 0 ? len : 1;
    _beg_pos = bp > 0 ? bp : 1;
    _next = _beg_pos - 1;
    }

    int main() {
    Triangular t1;
    return 0;
    }
    ```

    先说 C++的规则:

    1. 如果你没有提供<任何一个>构造器,编译器自动会为你提供 default constructor 。那么 “Triangular t1; ” 写法正确,并且它会调用默认的(编译器提供的)构造器给你用。

    2. 如果你提供了任意的一个非默认的构造器,比如 "Triangular(int a, int b);"。 那么 “Triangular t1; ” 写法错误。

    3. 如果你想要默认的构造器,但是你又想要自定义默认的构造器(由于你对编译器提供的那个默认构造器不满意)。你有两种方法:

    方法 A: 对已存在构造器参数提供默认值,也就你的这个:

    Triangular(int len = 1, int bp = 1);

    方法 B:对 C++默认的构造器重载,自定义它的实现:

    Triangular(); // 隐式声明:使用默认的糟糕器
    // Triangular()的代码实现 在别处

    切记:你有且只能拥有一个默认的构造器,你不可以想要拥有两个! 也就是说,方法 A 和方法 B,你只能选一个。

    而你的问题就出在这里:你实现了两个默认的构造器!如果存在两个可供选择的函数并且它们 [难分优劣] ,则编译器认为此次调用存在二义性并且报错。更具体的说,如果你同时实现了方法 A 和方法 B,当编译器执行到“Triangular t1; ” ,编译器即可以选择方法 A 定义的函数,也可以选择方法 B 定义的函数,编译器此时并不知道应该选择哪个最为恰当;编译器被搞晕了,它因此报错了。
    anonymous256
        11
    anonymous256   61 天前
    顺便提一句,你这个 C++类构造器相关的语言特性和设计完全无关,而是函数重载的范畴。编译器无法选择相应的函数。

    int fct(){return 1;}

    int fct(int a=1, int b=2) {return a+b;}

    int main() {
    auto a = fct();
    return 0;
    }

    这样的写法同样报错:call of overloaded ‘fct()’ is ambiguous !
    levelworm
        12
    levelworm   60 天前
    奇怪,照理说函数重载应该可以认出来这是俩函数啊,还是我记错了,擦。。。C++学的真不扎实。
    levelworm
        13
    levelworm   60 天前
    欧我知道是什么原因了,其实原因是你第二个 constructor 里头有默认值,所以 Triangular t1 可以调用两个中的任意一个。你把第二个构造器的默认值改掉就成了。我想呢,这样应该没问题啊。

    https://onlinegdb.com/rkSxDjPmO
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2941 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 05:06 · PVG 13:06 · LAX 22:06 · JFK 01:06
    ♥ Do have faith in what you're doing.