杂七杂八(2)——可以把重写看成是对函数的“重新赋值”

简介:
杂七杂八(2)——可以把重写看成是对函数的“重新赋值”
小序:
如此“不严谨”、如此“谬误”的标题,一看就是找骂的!
正文:
前几天在读代码的时候,发现代码里有一些函数,函数体是空的。起初是以为那是为了实现一个Interface或者是一个Abstract类而实际上又没什么实际用处才这么做的,于是没太当回事。今天Anstinus同学指导我写代码的时候,又用到这个“技术”,我才明白——这些函数体为空的函数都被声明为了virtual的,实际上“故意”留给子类去重写的(God~~哪个virtual函数不是“故意”留给子类的)。这个函数体为空的函数,在父类的某个逻辑流程中被调用了,只是因为函数体为空,而什么事都没有做。等到了子类中,一旦子类对这个函数进行了重写,就会个性化地影响到这个逻辑流程。
这种用法之所以特殊,是因为以前我使用virtual函数的时候,注重的是“对原有功能的改写”,这次使用virtual是对一个“根本没有功能”的函数的“功能”进行“改写”。这个感觉像什么呢?呃……以前的改写应该算做“从1到2”,而这次改写应该是“从0到1”。回到我们的标题——如果把函数看成是一个变量,函数的功能(函数体)就是它的值,而重写(override)就好像是对函数进行重新赋值一样,这样,在不同的继承级别上,函数就能获得不同的“值”。
在给出代码之前插一句:无论是变量名、函数名、类名……(简言之就是标识符啦),实际上都是指向一个内存地址,只是变量名指向的内存地址(再加上一个偏移量)里装的是“数值”,数据所占的内存块儿的大小由变量的类型决定;函数名指向的内存地址(再加上一个偏移量)里装的是一组CUP的指令;类名指向的内存地址里装的是这个类(无论是类还是实例)所共用的东西的清单(Table),所以,类实际上是一个作用域(Scope)。
OK,让我们看看今天我遇到的问题的简化版本——感谢Anstinus同学又教会我新东西:)
using  System;

namespace Sample
{
                class AAA
                {
                                public void DoSomething()
                                {
                                                Console.ForegroundColor = ConsoleColor.Yellow;
                                                Console.WriteLine("Do something...");

                                                // Something OPTIONAL
                                                this.DoSomethingOptional(); // 
调用了,但在 AAA 类中,实际上什么事也没做

                                                Console.ForegroundColor = ConsoleColor.Yellow;
                                                Console.WriteLine("Do other things...");
                                }

                                protected virtual void DoSomethingOptional()                                           // 
函数体是空的
                                {
                                }
                }

                class BBB : AAA
                {
                                protected override void DoSomethingOptional()
                                {
                                                Console.ForegroundColor = ConsoleColor.Green;
                                                Console.WriteLine("http://blog.csdn.net/FantasiaX"); 
                                }
                }

                class CCC : AAA
                {
                                protected override void DoSomethingOptional()
                                {
                                                Console.ForegroundColor = ConsoleColor.Magenta;
                                                Console.WriteLine("Some logic in CCC..."); 
                                }
                }

                class Program
                {
                                static void Main(string[] args)
                                {
                                                AAA aaa = new AAA();
                                                BBB bbb = new BBB();
                                                CCC ccc = new CCC();

                                                aaa.DoSomething();
                                                Console.WriteLine();
                                                bbb.DoSomething();
                                                Console.WriteLine();
                                                ccc.DoSomething();
                                }
                }
}

===============================
这是不是最佳方案?
实际上,几乎任何一个逻辑我们都有不止一种办法来实现它。就拿这个例子而言,完全可以定义一个
delegate void MyDelegate();
并为AAA类声明一个MyDelegate类型的field:
 
 
if (optionalThing != null)
{
        optionalThing();
}
这样,在子类中,随便你用什么方法,只要把恰当的函数名赋值给optionalThing就可以了。这种方法比起上面代码中的方法要灵活得多、自由得多!
你可能会问:那为什么项目中却没有采用这种方法呢?
原因有如下几个——
  • 为了使用这种方法,需要额外定义用途专一的delegate和成员变量,增加了日后维护成本
  • 两种方法在代码量上没有太大差别
  • 代码中的方法约束力比较强,这样,在子类中的实现看起来会比较一致,实现的时候和日后维护的时候成本较低(实际上,作为下游程序员,我也更喜欢这种方式,copy-paste就能解决很多问题)
 所以你看,有时候“缺点”反而是优点!
OK,今天就写到这儿~~做饭去喽!今天是西红柿炒鸡蛋:D









本文转自 水之真谛 51CTO博客,原文链接:http://blog.51cto.com/liutiemeng/95260,如需转载请自行联系原作者
目录
相关文章
|
6月前
|
Java Spring
使用枚举定义常量更好点儿
使用枚举定义常量更好点儿
|
3月前
|
前端开发 JavaScript 开发者
别再只用普通函数了!箭头函数的四大神奇区别,让你的代码飞起来!
【8月更文挑战第23天】在Web前端开发中,JavaScript的箭头函数(引入于ES6)提供了一种比传统函数更加简洁的定义方法。箭头函数使用 "=>" 替代 "function" 关键字,并且自动绑定外部 "this" 上下文,避免了传统函数中 "this" 值因调用方式不同而变化的问题。此外,箭头函数不拥有自己的 "arguments" 对象,但可以通过剩余参数语法获取所有参数。需要注意的是,箭头函数不能作为构造函数使用。理解这些差异有助于开发者编写更高效、清晰的代码。
142 0
|
6月前
|
C++
【C++小小知识点】重载、覆盖(重写)、隐藏(重定义)的对比【详解】(23)
【C++小小知识点】重载、覆盖(重写)、隐藏(重定义)的对比【详解】(23)
|
6月前
|
存储 索引 Python
什么是数组,什么是对象,并说出他们的区别
什么是数组,什么是对象,并说出他们的区别
44 6
|
6月前
|
JavaScript Java C++
定义和引用一堆数组
定义和引用一堆数组
37 1
|
6月前
|
存储 C++
定义一堆数组
定义一堆数组
79 1
|
6月前
|
编译器 C++ 开发者
【C++11保姆级教程】继承构造函数和花括号等式初始化器(brace-or-equal initializers)
【C++11保姆级教程】继承构造函数和花括号等式初始化器(brace-or-equal initializers)
258 0
|
6月前
this的含义,什么情况下使用this,改变this指针的两种办法。 === 由于this关键字很混乱,如何解决这个问题
this的含义,什么情况下使用this,改变this指针的两种办法。 === 由于this关键字很混乱,如何解决这个问题
43 0
|
存储 算法 编译器
【C++技能树】令常规运算符用在类上 --类的六个成员函数II
C++中为了增强代码的可读性,加入了运算符的重载,与其他函数重载一样
59 0
|
存储 安全 编译器
【是C++,不是C艹】 引用的概念 | 引用的使用 | 引用与指针的区别(二)
【是C++,不是C艹】 引用的概念 | 引用的使用 | 引用与指针的区别
98 0