C++代码复用的方法

情景:

  对不同的对象,要执行相同的逻辑操作。在C++中有哪些方法实现?


解决方法:

  1,模板,将不同的对象的类型作为模板参数。

//例:
int iarray[] = {2,6,4,8,3};
std::sort(iarray,iarray+sizeof(iarray)/sizeof(iarray[0]));
double farray[] = {2.0,6.0,4.0,8.0,3.0};
std::sort(farray,farray+sizeof(farray)/sizeof(farray[0]));

  2,使用宏,将不同类型的的对象作为宏参数。

//例:
#define max1(x,y) ((x) > (y) ? (x) : (y))
#define max2(x,y) ({typeof(x) _x = x; typeof(y) _y = y; _x > _y ? _x : _y;})
int    maxint    = max1(12,31);
int    l = 11, r = 30;
int    maxint2   = max2(++l,++r);
double maxdouble = max1(12.4,42.6);

  3,对不同的对象做一次抽象封装,提取公共的基类,在基类中抽象虚函数。

class Base
{
public:
	virtual void Operate() const = 0;
};

class Child1 : public Base
{
public:
	virtual void Operate() const {}
};

class Child2 : public Base
{
public:
	virtual void Operate() const {}
};

void Function(const Base* pObj)
{
	if(pObj)
	{
		pObj->Operate();
	}
}

注意:

  以上三种方法其实都可以归纳为C++的多态。前两种属于静态多态,第三种属于动态多态。所以都需要对操作的不同对象提供具有相同签名的可供调用的方法或者属性。

优缺点对比:

  1,模板

        优点:

         1)性能好,模板参数都是在编译时期已经特化并生成了制定类型的代码,而不是每次执行都需要动态处理,相对于动态多态来说少了运行时的类型检查和匹配。

         2)还有一些其他应用,比如编译期的断言实现,以及编译分支的分派。参考http://blog.sina.com.cn/s/blog_668aae7801017gm3.htmlhttp://blog.csdn.net/zdqflyfish/article/details/6728876

         3)对一个有整形数据成员的类,可以有两种定义方法:

template <int T_1, int T_2>
class class_template
{
public:
	class_template()
	: m_1(T_1)
	, m_2(T_2)
	{}
private:
	int m_1;
	int m_2;
};
class class_normal
{
public:
	class_normal(int p_1, int p_2)
		: m_1(p_1)
		, m_2(p_2)
	{}
private:
	int m_1;
	int m_2;
};
其实我们一直用的是第二种,而不会去使用第一种定义方法,第一种方法和第二种方法相比一个优势是,构造函数少了参数传递,从编译的汇编代码可以看出构造第一个类对象所需的操作要比第二个少,所以理论上来说性能要好于第二种类定义方法。
        缺点:

         1)相对于使用宏来说,模板只有两种使用模式,模板类和模板函数,所以一些代码用宏来写方便但是用模板来说却很复杂,需要额外处理很多的东西,比如如果要提取成函数则需要考虑到函数参数,并且直接提取成函数需要考虑的一个因素是return的含义变了,那么代码可能还需要涉及到重构。

         2)针对上述优点第三条所述,其实除了极少的优点(还包括例如参考链接中可以用于编译期检查的Int2Type)以外,缺点更多,首先,当每次使用class_templat<num1,num2> 来声明一个对象的时候,只要num1或者num2变了,就相当于重新申明了一种新的类型,那么class_template的类定义代码就需要再重复编译进代码中一次,这样就增加了最后生成的代码的占用空间。并且,对num1和num2的取值,类型都有特殊的要求,扩展性很窄。并且上面所说的运行时性能的提高只是理论说法,实际测试中对代码执行效率并不会有太大的影响。

         3)这个缺点主要体现在使用一些通过模版实现的包装类,如果被包装的类对象是一系列的具有多态关系的类,那么需要进行一些特殊处理才能使包装类也具有多态的性质(可以参考stl中的各种智能指针,例如两个类A和B,B继承自A,B和A具有多态的特性,std::shared_ptr<A>和std::shared_ptr<B>之间依然存在多态性)。现实中要实现起来其实并不简单,比如参考这么一种场景:

          因为设计需要,你的数据结构应该定义成“std::shared_ptr<YourPolymorphicType>”, 可以看到“YourPolymorphicType”并不会单独使用,而是放到智能指针里面的,所以为了从技术手段上限制其能够被以正确的方式使用,一个优先想到的解决方案是私有化这一系列类的构造函数,并且提供一个只能在堆上申请空间(new)的公有函数解决方案看似不错,但是由于“YourPolymorphicType”是一系列具有多态关系的类族,单纯的将基类构造函数改成private则子类构造无法调用基类构造函数导致编译错误,如果将基类构造函数改成public或者protected则由于提供了多态性,当业务扩展的时候“YourPolymorphicType”会膨胀,没有一个强制的机制保证新的子类一定按最初设计的样子来编写(实现成只能new的形式),因为单纯的依靠代码规范或者文档是没有办法做到严格约束不同coder行为的,毕竟不同的人水平不同,想法也不同。所以,新的方案是编写一个模板类“template<typename T> class TypeOnlyNew; ”(类似的数据结构定义方式早有先例,比如一种模板方式的单例类实现),对“YourPolymorphicType”实现放开限制,但是将最初的“std::shared_ptr<YourPolymorphicType>”改成“std::shared_ptr<TypeOnlyNew<YourPolymorphicType>>”,那么新的结构想要实现保证多态性就变得没那么简单了。


2,

        优点:

         1)简单明了(粗暴),只是代码替换,不像函数(其他两种方法都需要用到函数调用)那样在运行时需要增加额外的开销,同时也省去的封装函数需要考虑的依赖数据要用参数传递的方式,那么基于代码的可读性和优美度需要进行的一些重构工作。

         2)参考我的另一篇帖子:http://blog.csdn.net/u010300403/article/details/51701727应用2),简单明了的写法,大大简化了代码,并且提高了可读性(从某种程度来说),和代码的可维护性。

         3)宏的扩展代码,在不使用时是不会编译进代码中的,所以在某些情况下,相比(比如代码重构导致的)未调用的函数来说,不需要担心其会在生成的代码中造成冗余。但是实际编码最好不要出现没有调用的函数或者没有使用到的宏残存在代码中。

       缺点:

         1)必须要对宏的用法特别熟悉(清楚其原理(代码替换,编译期完成,不能递归)),才能使用得得心应手,而往往很多宏引起的错误就是因为对原理理解不清或者在复杂环境下并没能深刻理解其本质,比如讲宏和枚举搭配使用,将宏和一元运算符搭配使用,以及在嵌套宏调用中出现的扩展错误等,都是极其隐晦并且不容易理解的,所以使用需要慎重。

         2)难于调试,这时候只能保佑一大段的宏代码中没有出现bug,或者真的不幸撞上了,那就只能手动扩展宏展开到使用的地方,然后单步调试,但是如果宏规模很大或者嵌套层次很多,那就更不幸了。。

3,继承
        优点:

           1)面向对象编程语言的核心,优点不必多说了。

       缺点:

            1)C++继承多态是通过虚函数表来实现的,所以和模板,宏相比运行时性能会差。
  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值