6
回答
3种方法实现同一件事情
华为云实践训练营,热门技术免费实践!>>>   
探讨一段C++代码,

其中

1. 用类静态函数

2. 用友元函数

3. 用类的多态

请大家比较讨论这三种方式有啥优缺点,有什么不同的地方。

 
 
 
 
 
#include <stdio.h>

typedef void (*RUNONCE_PTR)(void* pThis);

class f
{
public:
	virtual void ff() = 0;
};

class A :  public f
{
public:
	A()
	{
		m_value = 1;
	}
	static void p(void* pThis)
	{
		A* pA = (A*)pThis;
		printf("this is %d\n", pA->m_value);
	}
	virtual void ff()
	{
		printf("this is %d\n", m_value);
	}
private:
	friend void ppA(void* pThis);
	int m_value;
};

void ppA(void* pThis)
{
	A* pA = (A*)pThis;
	printf("this is %d\n", pA->m_value);
}

class B : public f
{
public:
	B()
	{
		m_value = 2;
	}
	static void p(void* pThis)
	{
		B* pB = (B*)pThis;
		printf("this is %d\n", pB->m_value);
	}
	virtual void ff()
	{
		printf("this is %d\n", m_value);
	}
private:
	friend void ppB(void* pThis);
	int m_value;
};

void ppB(void* pThis)
{
	B* pB = (B*)pThis;
	printf("this is %d\n", pB->m_value);
}

class C
{
public:

	struct CCC
	{
		RUNONCE_PTR pFunc;
		void* pParam;
	};
	C() : m_id1(0), m_id2(0), m_id3(0)
	{
	}
	void Register1(RUNONCE_PTR pFunc, void* pParam)
	{
		CCC c;
		c.pFunc = pFunc;
		c.pParam = pParam;

		m_p1[m_id1++] = c;
	}

	void Register2(RUNONCE_PTR pFunc, void* pParam)
	{
		CCC c;
		c.pFunc = pFunc;
		c.pParam = pParam;

		m_p2[m_id2++] = c;
	}

	void Register3(f* pf)
	{
		m_p3[m_id3++] = pf;
	}

	void Run1()
	{
		for (int i=0; i<m_id1; i++)
		{
			m_p1[i].pFunc(m_p1[i].pParam);
		}
	}

	void Run2()
	{
		for (int i=0; i<m_id2; i++)
		{
			m_p2[i].pFunc(m_p2[i].pParam);
		}
	}

	void Run3()
	{
		for (int i=0; i<m_id3; i++)
		{
			m_p3[i]->ff();
		}
	}

private:
	int m_id1;
	int m_id2;
	int m_id3;

	CCC m_p1[2];
	CCC m_p2[2];

	f* m_p3[2];
};


int _tmain(int argc, _TCHAR* argv[])
{
	C c;
	A a;
	B b;
	c.Register1(A::p, &a);
	c.Register1(B::p, &b);
	c.Run1();

	c.Register2(ppA, &a);
	c.Register2(ppA, &b);
	c.Run2();

	c.Register3(&a);
	c.Register3(&b);
	c.Run3();

	return 0;
}



<无标签>
举报
little_kid
发帖于3年前 6回/318阅
共有6个答案 最后回答: 3年前

关于这三个“技术”有一些个人的看法,如果有不对的地方还请指正:

1、友元函数、友元类:如果发现一个设计中必须要用到这类东西的话,基本上可以确定这个设计是有问题的。友元的出现基本上就使得这个类的封装性大大退化,几乎退化到了结构体。(当然,也不乏Qt中为了加速绘制直接把变量public这样的存在)可以这么说:做设计的时候出现了friend就可以推倒重做了。(曾经因为friend是否有必要存在这个问题还跟老师吵过一架)

2、多态:本身只是个特性,在标准C++里面没什么太过在意的。Qt里面的槽函数或者信号函数要是多态的话就有点麻烦。构造函数的多态其实还是很欢迎的www,方便各种简化代码。不过用到模板的多态感觉要是写的不好的话很容易写的自己都不想看……

3、静态函数:这个……额……很好用?个人来说用的不亦乐乎。工厂、单例、做一些处理什么的非常的方便。

以上纯属个人见解,还请大神们轻拍砖。

看了看代码,“同一件事情”指的应该是:成员函数作为回调函数。

首先,需要考虑的是:是否必须使用回调函数这个方案。至少有两种情况必须如此:1.调用这个函数的代码,自己无法直接控制(第三方的库或者框架)2.成员函数被调用的机会很多,而且函数体实际执行的时间非常短,程序性能要求比较高,不可以使用虚函数。

不过,这种时候,完全没有必要使用friend或者静态函数(用静态函数只是to make the compiler happy)。

inline void ppA(void* pThis)
{
    A* pA = (A*)pThis;
    pA->ff();
}



用一个函数把成员函数包裹起来,ff也没有必要是虚函数了。

当然,使用lambda表达式是非常方便的方案。


除了上面两种情况,尽量用多态吧,毕竟是面向对象的程序。。。另外,std::function或者boost::function也是一种的选择。

1楼、2楼的回答非常专业,看得出来是有丰富经验的人。 1楼的许多观点我认同。使用类的静态函数比较方便安全。 

项目中A、B是两种不同的方法干同一种事情,C是加载配置文件的接口,A、B注册的回调是从公共配置i中拿走各自要的信息。我写代码时首先想到了多态的方法,但是我觉得A、B本来也许没多大关系,或者关系不明确时使用多态可能有点牵强。所以我首先选择了静态函数,其次选择了友元函数。

这个代码我在VS2013 x86上测试没有问题,抛除设计上的问题,这3中方法效果是相同的。友元的确不太合理,我这里用友元并不是为了获取类的访问权限,只是为了将函数声明在这个class里,这样读代码的人方便知道这个函数使用时和这个类相关的。这种权限完全可以用GET方法来实现的。

windows平台上有些特性不是特别了解,这代码我在编写动态库时遇到了问题。采用类的静态函数生成的动态库,我在LoadLibrary时加载失败,错误码是998(对内存位置的无效访问)。

我推测windows对类的静态函数生成的符号是不是不同于全局函数?我的理解类的静态函数就相当于全局函数。所以我用友元函数,这是个全局函数,应该没问题了吧。结果还是错误码是998。 

最后我换到了多态,LoadLibrary成功了。这段代码在正常的程序中或者静态编译是没问题的,但是不知为什么编写为windows下的DLL文件时只能使用多态方式。不知各位谁在windows下的经验丰富给解答一下。

2楼说的lamda表达式,函数式编程,我暂时不熟悉,现在还属于C++的新特性,为来可能有所作为。考虑到编译器的兼容性,暂时不是特别适合在正式的产品、项目中使用。lamda可能未来很好的一种方法。 感谢回答。

不知道有没有linux达人解释一下linux下有无类似问题

--- 共有 6 条评论 ---
東條・スペンサー・咲个人Lambda函数用的比较多的是在Qt中使用connect连接信号和槽函数时候,有些太小槽函数就可以省略掉不写了。感觉很是给力。 3年前 回复
東條・スペンサー・咲Lambda表达式是C++11中才开始引入的匿名函数(很多其它的语言在此之前就已经用了,例如Java、Python)。这个对于一些小函数的表达式来说看起来很是舒服。有一些函数就可以省掉了。例如std::sort函数最后的compare函数就可以用一行Lambda表达式代替。 3年前 回复
Ivnoidea写错,不是getgetproce,是getproceaddr 3年前 回复
Ivnoidea是loadlibrary失败,不是getgetproce失败?多态可以成功是因为函数地址存到了虚函数表中,通过偏移可以直接访问 3年前 回复
little_kid回复 @十分不强力 : 不是查找不到符号的问题,即使没写最多没有这几个函数。生成DLL后被别的程序调用LoadLibrary时出错,程序直接崩溃或者错误码为998。 3年前 回复

引用来自“little_kid”的评论

1楼、2楼的回答非常专业,看得出来是有丰富经验的人。 1楼的许多观点我认同。使用类的静态函数比较方便安全。 

项目中A、B是两种不同的方法干同一种事情,C是加载配置文件的接口,A、B注册的回调是从公共配置i中拿走各自要的信息。我写代码时首先想到了多态的方法,但是我觉得A、B本来也许没多大关系,或者关系不明确时使用多态可能有点牵强。所以我首先选择了静态函数,其次选择了友元函数。

这个代码我在VS2013 x86上测试没有问题,抛除设计上的问题,这3中方法效果是相同的。友元的确不太合理,我这里用友元并不是为了获取类的访问权限,只是为了将函数声明在这个class里,这样读代码的人方便知道这个函数使用时和这个类相关的。这种权限完全可以用GET方法来实现的。

windows平台上有些特性不是特别了解,这代码我在编写动态库时遇到了问题。采用类的静态函数生成的动态库,我在LoadLibrary时加载失败,错误码是998(对内存位置的无效访问)。

我推测windows对类的静态函数生成的符号是不是不同于全局函数?我的理解类的静态函数就相当于全局函数。所以我用友元函数,这是个全局函数,应该没问题了吧。结果还是错误码是998。 

最后我换到了多态,LoadLibrary成功了。这段代码在正常的程序中或者静态编译是没问题的,但是不知为什么编写为windows下的DLL文件时只能使用多态方式。不知各位谁在windows下的经验丰富给解答一下。

2楼说的lamda表达式,函数式编程,我暂时不熟悉,现在还属于C++的新特性,为来可能有所作为。考虑到编译器的兼容性,暂时不是特别适合在正式的产品、项目中使用。lamda可能未来很好的一种方法。 感谢回答。

不知道有没有linux达人解释一下linux下有无类似问题

windows的思维,和类unix与linux的差异比较大,但从你目前对设计目标的简单描述来看,你用c++这种方式,把可以简单的事情搞复杂了。不得不喷一句,形式主义害死人,如果形式主义挂上了“语法”的身份,就是害死一群人。整天不是在研究具体问题,而是在研究形式上的语法,对于一个问题处理有什么差异。。哈。

引用来自“little_kid”的评论

1楼、2楼的回答非常专业,看得出来是有丰富经验的人。 1楼的许多观点我认同。使用类的静态函数比较方便安全。 

项目中A、B是两种不同的方法干同一种事情,C是加载配置文件的接口,A、B注册的回调是从公共配置i中拿走各自要的信息。我写代码时首先想到了多态的方法,但是我觉得A、B本来也许没多大关系,或者关系不明确时使用多态可能有点牵强。所以我首先选择了静态函数,其次选择了友元函数。

这个代码我在VS2013 x86上测试没有问题,抛除设计上的问题,这3中方法效果是相同的。友元的确不太合理,我这里用友元并不是为了获取类的访问权限,只是为了将函数声明在这个class里,这样读代码的人方便知道这个函数使用时和这个类相关的。这种权限完全可以用GET方法来实现的。

windows平台上有些特性不是特别了解,这代码我在编写动态库时遇到了问题。采用类的静态函数生成的动态库,我在LoadLibrary时加载失败,错误码是998(对内存位置的无效访问)。

我推测windows对类的静态函数生成的符号是不是不同于全局函数?我的理解类的静态函数就相当于全局函数。所以我用友元函数,这是个全局函数,应该没问题了吧。结果还是错误码是998。 

最后我换到了多态,LoadLibrary成功了。这段代码在正常的程序中或者静态编译是没问题的,但是不知为什么编写为windows下的DLL文件时只能使用多态方式。不知各位谁在windows下的经验丰富给解答一下。

2楼说的lamda表达式,函数式编程,我暂时不熟悉,现在还属于C++的新特性,为来可能有所作为。考虑到编译器的兼容性,暂时不是特别适合在正式的产品、项目中使用。lamda可能未来很好的一种方法。 感谢回答。

不知道有没有linux达人解释一下linux下有无类似问题

引用来自“中山野鬼”的评论

windows的思维,和类unix与linux的差异比较大,但从你目前对设计目标的简单描述来看,你用c++这种方式,把可以简单的事情搞复杂了。不得不喷一句,形式主义害死人,如果形式主义挂上了“语法”的身份,就是害死一群人。整天不是在研究具体问题,而是在研究形式上的语法,对于一个问题处理有什么差异。。哈。
我们只讨论问题、探讨方法、寻求答案。如果比较擅长类unix系统,不妨仔细讲讲。我想最有资格批评我的是我的领导。大家来这里都是讨论问题,求知的,不是求喷的。大哥请息怒啊,轻喷。
顶部