免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: 三月廿七
打印 上一主题 下一主题

问两个高级问题, [复制链接]

论坛徽章:
0
11 [报告]
发表于 2011-10-11 08:23 |只看该作者
楼主你别这样……

论坛徽章:
1
白羊座
日期:2013-09-18 22:02:26
12 [报告]
发表于 2011-10-11 09:32 |只看该作者
在*.c和*.h最好不要放在一個目錄下,因為他們的意義是不一樣的。另外其他模塊需要調用該模塊的函數時,需要包含頭文件,如果頭文件和code文件在一起就會比較麻煩。

C語言重載可以使用...可變參。

论坛徽章:
0
13 [报告]
发表于 2011-10-11 09:41 |只看该作者
本帖最后由 三月廿七 于 2011-10-11 09:46 编辑
在*.c和*.h最好不要放在一個目錄下,因為他們的意義是不一樣的。另外其他模塊需要調用該模塊的函數時,需要 ...
file3 发表于 2011-10-11 09:32


假如有个 Sound(Sound.h, Sound.c)模块,  Input模块(key.h, key.c mouse.h, mouse.c)
现在想分为 Sound, Input两个文件夹作为两个独立子工程, 那么是不是还应该在 Sound文件夹下再划分为 src, inc 两个文件夹来分别放置.h, .c呢??, Input同理...

不考虑实际情况,理论上怎么放置比较合理??

论坛徽章:
0
14 [报告]
发表于 2011-10-11 11:04 |只看该作者
1、一般来说, .h 文件应放在统一的include目录里。

因为,遵循编程惯例的前提下, .h文件相当于接口和数据结构定义声明,它的作用是为了便于其它模块复用,必须提供给任何可能使用到它的人。
相反, .c文件可以完全隐藏,他人不需要知道,只要给他们编译好的.lib或.so即可。

我认为较科学的一种管理方案是:
project A
     |----include
     |----src
     -----lib

src 下可以再搞一些子目录,以便分模块存放源文件;编译生成的库则统一存放在lib目录。

另,c++模板文件较为特殊,一般来说应放在include目录



2、不要仿造其它语言的特性,仿制它们的功能。

很多人喜欢用C来模拟C++类,模拟它的继承、重载等等机制。

但必须记住,c不是c++。

硬要让它和c++表现类似的话,何不直接使用c++?至少比你用宏以及其它诘屈聱牙的变态语法模拟出来的那个四不像更稳定可靠(毕竟那么多人用那么多年了)、速度更快(多少人做了多少优化,而且还有编译器内置支持的优化)、语法更简洁易懂(用宏来模拟c++,怎么搞都是变态,怎么搞都没法杜绝所有不需要的副作用,哪里比得上编译器的关键字支持)

无论如何,用c来模拟c++,你只能得到一个比c++更坏无数倍的怪胎。

正确的做法有两个:
1、完全抛弃c++给你的影响,量身打造适合c自己的一套机制。

可以参考一下gtk的做法。它用c完全实现了面向对象,支持面向对象的各种特性,支持widget强类型检查。
但它的实现完全没有c++的影子,全是简洁易懂的c代码。

2、有限制的使用c++特性:或者仅接纳最基础最可靠最有用的(白名单法),或者抛弃不好的(黑名单法)——如google编码规范摈弃C++流一样。

论坛徽章:
0
15 [报告]
发表于 2011-10-11 13:21 |只看该作者
本帖最后由 三月廿七 于 2011-10-11 13:23 编辑
1、一般来说, .h 文件应放在统一的include目录里。

因为,遵循编程惯例的前提下, .h文件相当于接口和数 ...
狗蛋 发表于 2011-10-11 11:04


gtk 是如何处理重载的呢?
比较生成一个图片有以下几种方式,1> 根据路径名生成图片, 2> 从另一张图片中裁剪出一个图片, 3>根据像素数据生成一个图片>
creatImageByPath(), creatImageByBuffer(), creatImageByWhat ???

论坛徽章:
0
16 [报告]
发表于 2011-10-11 15:29 |只看该作者
gtk 是如何处理重载的呢?
比较生成一个图片有以下几种方式,1> 根据路径名生成图片, 2> 从另一张图片中裁剪出一个图片, 3>根据像素数据生成一个图片>
creatImageByPath(), creatImageByBuffer(), creatImageByWhat ???
三月廿七 发表于 2011-10-11 13:21



我想你可能是想用一个createImage(...)来统一所有这些接口。
这样,输入路径,就去读磁盘文件;输入图片和区域信息,就去图片中截取数据并生成图片;输入XX,就用XX方法得到图片……

实际上,针对这个问题来说,这个C++特性是没有必要的。

因为你还是必须用不同格式的输入参数,以便编译器选择正确的函数。如果你的设计无误的话,甚至连函数取名都没能节省,只是少打了几个字符而已。

比如,createImageByXXX 最终总是这样两步:
1、以XXX方式得到图片数据
2、用图片数据创建图片

这种问题在C里面,可以用链式调用解决:
createImage(loadImage(filename))          //从文件创建
createImage(clipImage(imageBuf, rect)) //从已有图片截取
createImage(xxImage(...))                   //以xx方式获得图片数据



至于完全等同于c++的重载形式,c并不能提供。当然你可以用可变参数函数/宏以及少量运行期性能代价来实现。

比如下面这个方案,不过它并不模仿C++的“自动选择合适函数”的效果,而是通过“自动类型转换”这个动作来实现。大概是这样:

newImage(IMAGE(xxObj))
或者:
newImage(xxObj)    //此时要在newImage开头调用IMAGE宏,函数声明也从前面的newImage(image *)改为newImage(void *)

其中xxObj是一个程序框架可以识别的对象,比如URL对象、IMAGE对象等等。这可以通过在对象中嵌入metadata实现:
struce URL
{
     METADATA typeid=MD_URL;
     char * data;
}

然后,IMAGE宏会查找MD_URL到MD_IMAGE的转换函数指针(也就是loadImage),完成URL到IMAGE的转换,保证newImage调用成功。
通过精心设计,这个IMAGE宏可以做到近乎无额外运行期开销(例如,利用MD_URL和MD_IMAGE的部分位计算偏移,然后直接调用转换函数列表中的对应函数)。

GTK提供有类似的类型转换/检查宏,这个实现模仿了GTK的风格,但GTK很可能根本就没做这种画蛇添足的东西。毕竟它只是做界面的,不是做编译器的,有这么一套宏提供强类型检查已经足够了。


总之,用一种语言的时候,最好能完全忘掉另一种。并且永远不要试图用一种语言去模拟另一种,尤其是不要去模拟语法糖。

论坛徽章:
0
17 [报告]
发表于 2011-10-11 15:41 |只看该作者
十十
日月
……嘛,适合卖萌。
作为惯例,.h如果不是公开接口,放src里也没什么奇怪的。

论坛徽章:
0
18 [报告]
发表于 2011-10-11 16:06 |只看该作者
函数指针可以搞重载!

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
19 [报告]
发表于 2011-10-11 16:07 |只看该作者
本帖最后由 OwnWaterloo 于 2011-10-11 16:28 编辑
比如,createImageByXXX 最终总是这样两步:
1、以XXX方式得到图片数据
2、用图片数据创建图片

这种问题在C里面,可以用链式调用解决:
createImage(loadImage(filename))          //从文件创建
createImage(clipImage(imageBuf, rect)) //从已有图片截取
createImage(xxImage(...))                   //以xx方式获得图片数据
狗蛋 发表于 2011-10-11 15:29


"以xxx方式得到图片数据"在C++里面还可以继续重载…… 比如:

createImage(imageDateFrom(filename))
createImage(imageDateFrom(imageBuf, rect))
createImage(imageDateFrom(...))

不是说一定需要这么设计。

但一旦将有类似含义的函数实现为一组重载而非不同名的函数, 再配合函数模板, 立马就威力无穷了。

template<typename It,typename F>
void for_each(It begin, It end, F f)
{
      for (; begin!=end; begin=next(begin)) f(elem(begin));
}

这段代码可以用于vector, list, deque, ... 以及任何满足next, elem 含义的序列 —— 实际STL是用 ++ 与 * 而非next/elem 重载
这是 nextOfVector/List/Deque, elemOfVector/List/Deque 不能表达的。

一旦采用 nextOfXXX 抽象就在这里了。
而 next(XXX ...) 可以配合其他机制使得抽象可以继续

注意啊……  不是说一定非得采用这种设计, 一定要将load/clip 抽象为 dataFrom。
只是说, 当确实需要这种抽象(比如iterator)时, 缺乏重载带来的影响是没法消除的。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
20 [报告]
发表于 2011-10-11 16:16 |只看该作者
比如下面这个方案,不过它并不模仿C++的“自动选择合适函数”的效果,而是通过“自动类型转换”这个动作来实现。大概是这样:

newImage(IMAGE(xxObj))
或者:
newImage(xxObj)    //此时要在newImage开头调用IMAGE宏,函数声明也从前面的newImage(image *)改为newImage(void *)

其中xxObj是一个程序框架可以识别的对象,比如URL对象、IMAGE对象等等。这可以通过在对象中嵌入metadata实现:
struce URL
{
     METADATA typeid=MD_URL;
     char * data;
}

然后,IMAGE宏会查找MD_URL到MD_IMAGE的转换函数指针(也就是loadImage),完成URL到IMAGE的转换,保证newImage调用成功。
通过精心设计,这个IMAGE宏可以做到近乎无额外运行期开销(例如,利用MD_URL和MD_IMAGE的部分位计算偏移,然后直接调用转换函数列表中的对应函数)。
狗蛋 发表于 2011-10-11 15:29


能详细说说么? 特别是几乎无额外运行期开销? 几乎到什么程度?

如果是C++重载:
createImage(filename)
createImage(buf, rect)
createImage(xxx)

就是直接调用3个不同的函数。

还有其他一些方面, 比如: 任何一个函数都是独立的。 create(filename) 并不知道另外两个重载。 当它们产生或者删除时不会影响到create(filename)。
新添加一个重载也不需要 —— 重载机制不需要 —— 改变已有的重载。
而IMAGE宏以及相关的各种东西, 当新增加MD_xxx时, 需要做哪些修改?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP