一次调用适合多个重载方法现象研究

博客围绕编译器选择重载方法展开。通过具体程序示例,展示了在实参有多个适用重载方法时的不同情况,包括实参引用对象类型不同、基类和接口情况等。最后总结出编译器选择重载方法的四条原则,如优先匹配类型、选最近基类等。

调用重载的方法时,编译器会根据实参的数量和类型的不同选择调用适合的方法。那么如果一次调用使用的实参有多个重载方法可以适合时,编译器会怎么选择呢?见下面的程序。

class Base{}

class
Derive : Base{}

class
DDerive : Derive{}

public
class TestMain
{
    [STAThread]
    static void Main(string[] args)
    {
        DDerive dd = new DDerive();
        Base b = dd;
        Derive d = dd;

        TestMothed(b);
        TestMothed(d);
        TestMothed(dd);
    }
    static void TestMothed(Base b)
    {
        Console.WriteLine("Base");
    }
    static void TestMothed(Derive d)
    {
        Console.WriteLine("Derive");
    }
}

Base{}

class
Derive : Base{}

class
DDerive : Derive{}

public
class TestMain
{
    [STAThread]
    static void Main(string[] args)
    {
        DDerive dd = new DDerive();
        Base b = dd;
        Derive d = dd;

        TestMothed(b);
        TestMothed(d);
        TestMothed(dd);
    }
    static void TestMothed(Base b)
    {
        Console.WriteLine("Base");
    }
    static void TestMothed(Derive d)
    {
        Console.WriteLine("Derive");
    }
}

上面程序可以看到,第2次和第3次的TestMothed方法调用显然两个TestMothed重载方法都适用。而第1次TestMothed的调用虽然实参b的类型是Base类,但b引用的对象则是DDerive类。这三次方法调用会调用哪个重载方法呢?运行结果如下:

    Base
    Derive
    Derive

再把代码修改一下:

class Base{}

interface IInterface{}

class
Derive : Base, IInterface{}

public
class TestMain
{
    [STAThread]
    static void Main(string[] args)
    {
        Derive d = new Derive();

        TestMothed(d);
    }
    static void TestMothed(Base b)
    {
        Console.WriteLine("Base");
    }
    static void TestMothed(IInterface i)
    {
        Console.WriteLine("IInterface");
    }
}

这段代码根本不能编译,因为Base类和IInterface接口都是Derive类的基类(接口)且离Derive类一样近。

再把代码修改一下:

class Base{}

class
Derive : Base{}

class
DDerive : Derive{}

public
class TestMain
{
    [STAThread]
    static void Main(string[] args)
    {
        DDerive dd = new DDerive();
        TestMothed(dd, dd);
    }
    static void TestMothed(Base b, Derive d)
    {
        Console.WriteLine("Base, Derive");
    }
    static void TestMothed(Derive d, Base b)
    {
        Console.WriteLine("Derive, Base");
    }
}

Base{}

class
Derive : Base{}

class
DDerive : Derive{}

public
class TestMain
{
    [STAThread]
    static void Main(string[] args)
    {
        DDerive dd = new DDerive();
        TestMothed(dd, dd);
    }
    static void TestMothed(Base b, Derive d)
    {
        Console.WriteLine("Base, Derive");
    }
    static void TestMothed(Derive d, Base b)
    {
        Console.WriteLine("Derive, Base");
    }
}

这段代码同样不能编译,因为两个TestMothed重载方法的形参离实参都是一近一远。

从结果我们可以发现,如果一次调用使用的参数有多个重载方法可以适合时,编译器将根据以下原则选择重载方法:

1、优先选择形参与实参类型严格匹配的重载方法,而实参所引用的对象类型不以考虑;
2、当有多个重载方法的形参都是实参的基类(接口)时,则选择形参是离实参最近的基类(接口)的重载方法。
3、当有多个重载方法的形参都是实参的基类(接口)时,不能同时存在两个形参是离实参最近的基类(接口)的重载方法,否则不能编译。
4、若存在两个拥有两个以上形参的重载方法,它们有两个形参分别离实参一近一远,则不能编译。

<think>好的,我现在需要解决蓝桥杯单片机项目中数码管显示溢出的问题。首先,我得先理解什么是数码管显示溢出。根据用户提供的引用内容,尤其是引用[2]和引用[4],似乎这个问题与动态显示数码管的刷新频率有关。可能是在主循环或中断处理中,数码管显示函数被调用不当,导致显示数据未能及时刷新,从而出现显示错乱或残影,即“溢出”。 用户提到的引用[2]中提到,在超声波测距的while循环中调用数码管显示函数可能导致计时错误,进而影响显示。这说明当主循环中的某些操作(如超声波测距的延时)占用了过多时间,导致数码管的动态扫描被中断,无法按时刷新,从而出现显示异常。此外,引用[4]中的示例代码展示了使用定时器中断来管理数码管的显示时间,通过定时器中断定期刷新数码管,保证其稳定显示。如果定时器的配置不正确,比如中断间隔过长或代码执行时间过长,可能导致数码管刷新不及时,出现溢出。 接下来,我需要分析可能的原因。首先,动态数码管显示依赖于定时中断来轮流点亮各个数码管,每个数码管的点亮时间必须足够短,利用人眼的视觉暂留效应形成稳定显示。如果中断服务程序(如引用[4]中的ServiceT0)执行时间过长,或者中断间隔设置不合理,会导致某些数码管点亮时间过长,而其他数码管无法及时刷新,出现部分段码残留或闪烁。 另一个可能的原因是在主循环中有耗时操作,如超声波测距中的长时间循环(引用[2]),这会阻塞主程序,导致数码管的动态扫描无法及时进行,从而显示异常。例如,如果在等待超声波回波时没有及时处理数码管显示,数码管的显示数据得不到更新,可能出现显示内容停滞或错乱。 此外,代码中的变量管理也可能导致问题。例如,引用[4]中的countSMG变量用于控制显示状态,如果该变量在中断和主程序中被不当修改,可能导致显示逻辑错误,进而溢出。 解决方案方面,首先应该优化中断服务程序,确保定时器中断的间隔合理,并且中断服务程序中的代码尽量简短,只处理必要的显示切换,避免复杂计算或长时间操作。例如,在ServiceT0中,只更新数码管的位选和段码,而不进行其他耗时操作。 其次,需要检查主循环中的阻塞代码,如超声波测距中的while循环。根据引用[2]的建议,应避免在需要精确计时的循环中调用数码管显示函数,而是通过中断或其他非阻塞方式处理显示,确保主循环不会长时间阻塞,从而保证数码管的定时刷新不受影响。 另外,可能需要调整数码管的刷新频率。根据引用[4]中的示例,定时器中断设置为1ms(1000微秒),而countSMG每1000次中断(即1秒)重置。这可能用于控制某些显示效果,但数码管的动态刷新通常需要更高的频率,比如每秒60Hz以上,即每个数码管的点亮时间不超过几毫秒。如果定时器中断间隔过长,会导致每个数码管的显示时间过长,产生闪烁或残影。因此,调整定时器的重载值,缩短中断间隔,可能有助于解决溢出问题。 同时,检查数码管显示函数的代码是否正确。例如,在引用[4]的Showdate函数中,根据countSMG的值切换显示内容,如果逻辑错误可能导致某些段码在不应该显示的时候被激活,造成显示混乱。确保段码和位选的赋值正确,并且在不需要显示时正确关闭。 最后,考虑资源冲突或硬件问题。例如,如果多个模块(如超声波、按键、数码管)共享同一个定时器或IO端口,可能导致中断冲突或信号干扰,影响数码管的显示。需要检查硬件连接和资源配置,确保各模块协同工作不冲突。 总结可能的原因和对应解决方案: 1. 中断处理不当:优化定时器中断服务程序,确保快速执行,调整中断间隔。 2. 主循环阻塞:避免在耗时操作中调用显示函数,使用中断或状态机非阻塞处理。 3. 刷新频率不足:调整定时器设置,提高刷新频率。 4. 变量管理错误:检查countSMG等变量的使用,确保正确更新和范围控制。 5. 代码逻辑错误:验证显示函数中的逻辑,确保段码和位选正确赋值。 6. 资源冲突:检查硬件配置,避免中断或端口冲突。</think>### 数码管显示溢出的原因及解决方案 #### 一、原因分析 1. **中断处理时间过长** 定时器中断服务程序(如引用[4]中的`ServiceT0`)若执行时间过长,会导致数码管刷新不及时。动态显示需要严格周期性的中断,若中断间隔过长或代码冗余,会导致显示残影或闪烁。 2. **主循环阻塞** 在主循环中执行耗时操作(如超声波测距的`while`循环[^2]),会阻塞数码管动态扫描,导致显示数据无法更新,出现显示停滞或错乱。 3. **刷新频率设置不当** 动态数码管的刷新频率通常需高于50Hz(即每帧<20ms)[^4]。若定时器中断间隔过长(如示例中1ms中断但`countSMG`累计到1000次才重置),实际刷新频率可能不足。 4. **变量管理错误** 控制显示的变量(如`countSMG`)若未正确初始化或超出范围,可能导致显示逻辑错乱。 --- #### 二、解决方案 1. **优化中断服务程序** - **缩短中断间隔**:调整定时器重载值为更小值(如500μs),提高刷新频率。例如: ```c TH0 = (65535 - 500) / 256; TL0 = (65535 - 500) % 256; ``` - **简化中断代码**:仅保留数码管位选切换和段码赋值,避免复杂计算。 2. **避免主循环阻塞** - **非阻塞式超声波测距**:使用状态机或标志位代替`while`循环等待回波。例如: ```c if (超声波触发标志) { // 处理测距并退出,不阻塞主循环 } ``` - **分离显示与功能逻辑**:将数码管显示函数放在定时器中断中执行,而非主循环。 3. **调整刷新频率** - 确保每个数码管的点亮时间均匀且短暂。例如,8位数码管以2ms中断间隔轮询,则每位数码管刷新频率为: $$ \frac{1}{2ms \times 8} = 62.5Hz $$ 满足无闪烁要求。 4. **规范变量管理** - 对`countSMG`等控制变量进行范围约束,例如: ```c if (countSMG >= 8) countSMG = 0; // 8位数码管轮询 ``` 5. **硬件检查** - 确认段码和位选信号无硬件冲突(如IO口驱动能力不足导致显示暗淡或残留)。 --- #### 三、示例代码修正 ```c // 定时器中断服务程序(优化后) void ServiceT0() interrupt 1 { TH0 = (65535 - 2000) / 256; // 2ms中断间隔 TL0 = (65535 - 2000) % 256; // 关闭所有数码管(消隐) P2 = (P2 & 0x1F) | 0xE0; // 位选控制 P0 = 0xFF; // 段码全灭 // 切换到下一位数码管 Seg_pos = (Seg_pos + 1) % 8; P2 = (P2 & 0x1F) | (0x80 >> Seg_pos); P0 = Seg_code[Seg_pos]; // 更新段码 } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值