Time 类的案例学习--具有默认实参的构造函数

本文通过一个具体的Time类实例,展示了如何为构造函数设置默认参数,并通过不同的构造方式创建对象,验证了构造函数中参数的有效性和默认值的正确使用。

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

演示如何隐式的将形参传递给构造函数

Time.h

#ifndef TIME_H
#define TIME_H
class Time{
public:
    Time(int=0, int=0, int=0);
    void setTime(int, int, int);
    void setHour(int);
    void setMinute(int);
    void setSecond(int);

    int getHour();
    int getMinute();
    int getSecond();

    void printUniversal();
    void printStandard();
private:
    int hour;
    int minute;
    int second;
};
#endif

Time.cpp

#include<iostream>
#include<iomanip>
#include"Time.h"
using namespace std;

Time::Time(int hr,int min,int sec){
    setTime(hr, min, sec);
}
void Time::setTime(int h, int m, int s){
    setHour(h);
    setMinute(m);
    setSecond(s);
}
void Time::setHour(int h){
    hour = (h >= 0 && h < 24) ? h : 0;
}
void Time::setMinute(int m){
    minute = (m >= 0 && m <= 60) ? m : 0;
}
void Time::setSecond(int s){
    second = (s >= 0 && s <= 60) ? s : 0;
}
int Time::getHour(){
    return hour;
}
int Time::getMinute(){
    return minute;
}
int Time::getSecond(){
    return second;
}
void Time::printUniversal(){
    cout << setfill('0') << setw(2) << getHour()<<":"<<setw(2)<<getMinute()<<":"<<setw(2)<< getSecond();
}

测试.cpp

#include<iostream>
#include"Time.h"
using namespace std;
int main(){
    Time t1;//all arguments defaulted省略所有实参
    Time t2(2);
    Time t3(21,34);
    Time t4(12, 25, 42);
    Time t5(27, 74, 99);
    cout << "Constructed with:\n\nt1:all arguments defaulted\n";
    t1.printUniversal();
    cout << "\n";
    t1.printStandard();

    cout << "\n\nt2:hour specified:minute and second defaulted\n";
    t2.printUniversal();
    cout << "\n";
    t2.printStandard();

    cout << "\n\nt3:hour and minute specified:second defaulted\n";
    t3.printUniversal();
    cout << "\n";
    t3.printStandard();

    cout << "\n\nt4:hour and minute and second all specified\n";
    t4.printUniversal();
    cout << "\n";
    t4.printStandard();

    cout << "\n\nt5:all are invalid elements\n";
    t5.printUniversal();
    cout << "\n";
    t5.printStandard();
    cout << endl;
 

    cout << endl;

}

 

<think>好的,用户问的是为什么C++中的半缺省参数必须从右往左依次给出,不能间隔着给。首先,我需要回忆一下C++的函数参数规则,特别是关于默认参数的部分。 用户之前已经问过关于声明和定义中默认参数冲突的问题,现在这个问题是关于半缺省参数的顺序。可能需要从函数调用的匹配机制来解释。半缺省参数指的是函数参数中一部分有默认值,另一部分没有的情况。 为什么必须从右往左给出呢?可能涉及到函数调用时参数传递的顺序和匹配方式。比如,当调用函数时,参数是从左到右依次传递的,如果有默认参数在中间,那么左边的参数如果没有默认值,就必须显式传递,否则会导致参数不匹配。例如,如果函数是void func(int a, int b=10, int c),这样在调用的时候,如果只传两个参数,就会有问题,因为第三个参数c没有默认值,但中间参数b有默认值,这时候可能无法正确匹配。 所以,规定半缺省参数必须从右往左,可以确保在调用时,左边的参数必须显式传递,而右边的可以省略。比如void func(int a, int b, int c=10),这样调用时,传a和b,c可以省略;或者只传a,b和c用默认值的话不行,但如果有void func(int a=5, int b, int c=10)就会有问题,因为中间的b没有默认值,导致调用时无法正确省略参数。 还需要考虑编译器的解析方式。当函数被调用时,参数是按照从左到右的顺序来匹配的。如果允许间隔设置默认参数,可能会导致某些参数无法正确匹配,因为中间的非默认参数必须被传递,而右边的参数可能有默认值,这样调用时可能会出现参数数量不足的情况,或者参数型不匹配。 例如,假设允许void func(int a=1, int b, int c=3),当调用func(2)时,a被赋值为2,但b没有默认值,这时候就会缺少参数,导致错误。所以为了避免这种歧义,C++规定必须从右往左设置默认参数,确保没有间隔,这样调用时只能省略右边的参数,左边的必须显式传递,从而保证参数传递的明确性。 另外,可能需要提到函数重载的情况,如果允许间隔默认参数,可能会导致函数重载解析的复杂性增加,编译器难以确定最佳匹配,从而引发二义性错误。因此,这种规定也是为了简化编译器的处理逻辑,确保函数调用的明确性和一致性。 总结来说,主要原因包括:确保函数调用时参数传递的明确性,避免参数匹配错误,简化编译器解析过程,以及防止二义性情况的出现。这些因素共同决定了半缺省参数必须从右往左依次给出的规则。</think>### 一、核心规则解析 **强制顺序要求**:C++强制规定,当函数参数**部分有默认值**(半缺省参数)时,必须从参数列表的**最右端开始连续设置**,禁止跳跃式或左端优先设置。这是由函数调用的**参数匹配机制**决定的。 --- ### 二、错误场景演示 #### 非法写法1(中间穿插默认参数) ```cpp void Draw(int x=0, int y, int color=255); // ❌ 错误:x有默认值但y没有 ``` - **调用困境**: 若调用`Draw(5)`,编译器无法确定这个`5`对应`x`还是`y` (`x`已有默认值,但中间的`y`没有默认值) #### 非法写法2(左端优先) ```cpp void Connect(string ip="localhost", int port); // ❌ 错误:左端参数有默认值 ``` - **调用困境**: 调用`Connect()`时,`ip`有默认值但`port`没有 → **参数数量不足** --- ### 三、底层原理剖析 #### 参数匹配机制 1. **严格左→右匹配原则** 函数调用时,实参按**从左到右顺序**依次填充形参: ```cpp // 正确写法 void Send(int timeout, string msg = "hello", bool encrypt = false); // 调用示例 Send(1000); // timeout=1000, msg="hello", encrypt=false Send(2000, "warning"); // timeout=2000, msg="warning", encrypt=false ``` - **不可逆填充**:每个实参必须对应左边第一个未匹配的形参 2. **跳跃禁止的必然性** 如果允许间隔设置默认参数: ```cpp // 假设允许非法写法 void F(int a=1, int b, int c=3); // ❌ 虚构场景 F(2); // 编译器困惑: // 是 a=2 → 但中间的b没有默认值? // 还是 a=1 → b=2?但c仍有默认值? ``` - **二义性崩溃**:编译器无法确定参数对应关系 --- ### 四、正确写法模式 #### 从右往左连续设置 ```cpp // ✅ 合法写法:默认参数连续位于右端 void Render(int width, int height, int fps=60, bool fullscreen=false); // 调用示例 Render(1920, 1080); // fps=60, fullscreen=false Render(1280, 720, 30); // fullscreen=false Render(800, 600, 30, true); // 全参数显式指定 ``` #### 全缺省参数特例 ```cpp // ✅ 所有参数均有默认值(仍需保持左→右逻辑) void Alert(string msg = "Error", int code = 999); // 调用示例 Alert(); // msg="Error", code=999 Alert("Warning"); // code=999 Alert("Critical", 500); // 全显式指定 ``` --- ### 五、设计哲学解读 #### 语言设计者的权衡 - **降低歧义**:确保每个实参都能**明确对应到最左端需要值的形参** - **简化编译器实现**:线性匹配机制避免复杂推导 - **提升代码可读性**:参数默认值的设置位置直观反映调用时的省略模式 --- ### 六、进阶边界案例 #### 与函数重载的交互 即使通过函数重载模拟“间隔默认参数”,仍然会产生二义性: ```cpp // 尝试用重载模拟非法效果 void Process(int a) { Process(a, 0, 255); } // 重载1 void Process(int a, int b, int c=255) { /*...*/ } // 重载2 Process(5); // 编译器无法确定调用重载1还是重载2 → ❌ 二义性报错 ``` #### 构造函数初始化列表限制 ```cpp class Config { public: // 构造函数参数也必须遵守右端默认规则 Config(int version, string env="dev", bool debug=false); // ✅ // Config(string env="dev", int version, bool debug=false); ❌ 非法 }; ``` --- ### 七、错误对照表 | 错误型 | 示例代码 | 编译器反应 | |-------------------------|----------------------------------|----------------------------| | 中间参数有默认值 | `void F(int a=1, int b, int c)` | `error: missing default` | | 左端优先默认参数 | `void F(string s="", int code)` | `error: default missing` | | 默认参数不连续 | `void F(int a, int b=5, int c)` | `error: non-default` | 掌握这一规则可避免90%以上的默认参数相关编译错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小菜鸡变形记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值