自己封装常用代码时所面临的困境(一起 发布时间: 2013-01-18 10:22:42 作者: rapoo
自己封装常用代码时所面临的困境(一起来讨论如何构建个人c++库) 本人工作近1年了,目前只负责过一些简单的设备接口的封装,都很简单,没什么技术含量。时间久了,发现自己做的很多事都是重复的,所以打算自己封装一个个人库。着手开始做的时候,才感觉心有余而力不足。 我的理念是,尽可能把库写地精巧,进行浅层次封装,但又要方便自己使用。如果能不引入c++类的地方就不引入类。但是,我发现自己对类的依赖性太强了,写下面一个简单的函数时就不知道怎么办了:
#define EG_DLL_EXPORT #include"FileIO.h" int EG::LoadToBuffer(const char* FileName,int offset,TagType FTTag,byte* buffer) { FILE* f=fopen(FileName,"rb"); if(!f) return FAILED_TO_OPEN; //文件打开失败 fseek(f,offset,SEEK_CUR); TagType Tag; fread(&Tag,sizeof(TagType),1,f); if(Tag!=FTTag) return INVALID_DATATYPE; //文件类型不正确 int fileSize=_filelength(_fileno(f))-offset; buffer=new byte[fileSize]; fread(buffer,fileSize,1,f); fclose(f); return OK; }这个函数的目的是:把文件数据一次性加载到内存中。
调用方式是:返回值说返回的是错误信息,byte* buffer 是出参,用户只要提供文件路径FileName、有效字节偏移量offset、文件类型标签FTTag,和一个用于获得数据的buffer就行了。
说到这里,一定会有前辈批评我为什么要在函数内部对buffer进行堆内存分配。这就是问题的开始了。
我之所以这样做,是因为我希望分配和文件大小一致的buffer。如果放在外面分配内存,我就无从得到这个文件大小了,如果说要我定义一个全局变量,来存储filesize,那样做的话,和没有封装就没什么两样了。 当然,如果使用类的话,这个问题可以很容易解决。问题是我前面说了,不希望引入类和继承之类的降低性能的机制,我只打算建立一个小型易用的库。
请问各位前辈有什么好的建议?
另外,内存分配是不是最好要遵循谁使用谁释放的原则?
最近很少回帖,都没可用分了,后面我会追加分数! 私人库? dll
[解决办法] >不希望引入类和继承之类的降低性能的机制
class甚至继承对性能不是没有任何影响,就是小到几乎任何状况都可以忽略
你所谓的开销主要是来自virtual
利用class进行浅封装不会对性能构成影响的
[解决办法] 我的看法是,只要不过多的使用类的继承和多态,为什么不用类的方式呢?类本来就是封装的好手。
个人建议,建立你的几个基本类,然后以这些基本类为基础,去增加你的扩展工具类。注意,这里以基本类为基础,不是说从它们派生,而是将这些基本类作为工具,尽量做到之间的耦合只限于基本类的对象或指针,这样或者是一个累积个人类库的方法。
另,内存分配建议最好是谁申请谁释放。你的例子中,为什么不用智能指针呢?
[解决办法] 你用不用class跟你的性能高不高没有绝对的关系
既然要用c++开发高性能的库
花时间搞懂c++的有的那些抽象机制
需要付出那一些代价是很值得的
要讲的东西太多,我觉得你还是直接去读一读c++ primer 5好了
然后是effective c++, more effective c++
及exceptional c++.觉得太多书要读可以直接看
C++ Coding Standards: 101 Rules, Guidelines, and Best Practices
c++提供的抽象机制不是全部都要付出执行时间的代价的
如果要用smart pointer,可以优先考虑unique_ptr
shared_ptr可以等你有必要使用的时候再用
觉得自己功力还不错的话可以挑战一下及inside the c++ object model
如果觉得要读这么多书很烦,不想花时间学习
直接用c就好了,c++的学习时间确实比较漫长
[解决办法] 如果不嫌累的话,effective stl也可以看一看
题外话,我从来没看过有那个c++的大牛大力推荐
大家大量使用继承的,反倒是看过herb,meyers,
bjarne等人建议大家不要经常使用继承
但是现实社会的例子是举凡支援oo的语言
一定到处都是继承,不管有没有继承的必要
我就是要继承,这真是一个无比诡异的现象
[解决办法] 返回 vector<unsigned char>不好么
[解决办法] 继承是OO的基础设施,如果没有继承,就无法实现多态性,而多态性是OO的手段。所以任何讲OO的文章,都会涉及继承。不要从字面或语法角度看“避免使用继承”这个忠告,而应该从设计的角度出发来看。
Andrei Alexandrescu说“设计就是做选择“。设计模式研究OOP类体系之间的关系和方法,它的两个基本手段是继承和合成。前者属于静态设计方式,在类级别上复用代码,在编译期决定设计。后者属于动态设计方法,在类对象级别上复用代码,在运行期灵活切换。不同的设计模式选择不同的方式或两者的结合来解决模块的表示、交互问题。满足现代软件开发的各种需求。
但就算是采用组合方式来构建系统,为了进行恰当的运行时功能切换,也需要让组合对象有一个多态性体系,否则你无法保持接口的一致性。这还是基于继承的——至少你的组合对象体系必须是多态的。所以OOP离不开继承,因为它是基础。
------解决方案--------------------
[解决办法] 好吧,我又想说老生常谈的KISS原则了。 反正就是解耦了,多组合少继承。尽量还是一个函数一个功能,我以前总担心明明一个函数可以做两个功能虽然会杂糅一点,但是比拆开性能高一点,后来才体会到性能瓶颈一般都不是自以为的地方,差那么点点也无所谓啦,逻辑简单了,耦合低,实在不行大不了再重构整合嘛[解决办法] 引用: 继承是OO的基础设施,如果没有继承,就无法实现多态性,而多态性是OO的手段。所以任何讲OO的文章,都会涉及继承。不要从字面或语法角度看“避免使用继承”这个忠告,而应该从设计的角度出发来看。 Andrei Alexandrescu说“设计就是做选择“。设计模式研究OOP类体系之间的关系和方法,它的两个基本手段是继承和合成。前者属于静态设计方式,在类级别上复用代码,在编…… virtual和继承若妥善利用 可以让代码变得更好,可惜今天滥用继承的情况还是占多数 一堆人设计class的时候根本是为了继承而继承 而不是为了接口的一致,执行期间的多态性而这么做 我不讨厌继承和virtual 但是今日滥用继承的现象实在是太普遍 几乎已经到了只要是OO就一定要继承的地步 >继承是OO的基础设施,如果没有继承,就无法实现多态性 执行期间的多态性,不一定要靠继承来完成,例如 void poly_function(int time, int state, std::function<void(int, double)> const &func); >前者属于静态设计方式,在类级别上复用代码,在编译期决定设计。后者属于动态设计方法 前后者弄反了?[解决办法] 个人感觉还是弄成动态库,比较好[解决办法] 1.利用智能指针。 2.利用vector<char>代替指针。 int EG::LoadToBuffer(const char* FileName,int offset,TagType FTTag, std::vector<char>& buffer)//修改掉最后一个参数 { FILE* f=fopen(FileName,"rb"); if(!f) return FAILED_TO_OPEN; fseek(f,offset,SEEK_CUR); TagType Tag; fread(&Tag,sizeof(TagType),1,f); if(Tag!=FTTag) return INVALID_DATATYPE; int fileSize=_filelength(_fileno(f))-offset; //buffer=new byte[fileSize]; //fread(buffer,fileSize,1,f); buffer.resize(fileSize); //增加这2行代码。 fread(&buffer[0],fileSize,1,f); fclose(f); return OK; } //外面读取的时候这样写就行了,不必要纠结内存分配 int main() { vector<char> cv; LoadToBuffer(........., cv); //用的时候就是强转(byte*)&cv[0] }[解决办法] 引用: 引用:继承是OO的基础设施,如果没有继承,就无法实现多态性,而多态性是OO的手段。所以任何讲OO的文章,都会涉及继承。不要从字面或语法角度看“避免使用继承”这个忠告,而应该从设计的角度出发来看。 Andrei Alexandrescu说“设计就是做选择“。设计模式研究OOP类体系之间的关系和方法,它的两个基本手段是继承和合成。前者属…… 很多人滥用继承,这个是存在的。所以才需要正确认识多态,做正确选择。 继承属于静态设计,在运行期就不能改变了。合成可以在运行期通过基类指针或引用改变,因此是动态的。没弄反。 你这个模板多态的例子本身没问题,但只能作为一个不用继承产生多态的例子。但无法在类体系之间使用,无法应用于大量的模式,以适应大规模软件开发的需求。在某些场合下用可以,但局限性很大。[解决办法] 引用: 返回 vector<unsigned char>不好么 [解决办法]
引用: 引用:引用:继承是OO的基础设施,如果没有继承,就无法实现多态性,而多态性是OO的手段。所以任何讲OO的文章,都会涉及继承。不要从字面或语法角度看“避免使用继承”这个忠告,而应该从设计的角度出发来看。 Andrei Alexandrescu说“设计就是做选择“。设计模式研究OOP类体系…… 动态设计和静态设计,不是动态绑定和静态绑定。。。[解决办法] 可维护性、可靠性比性能重要![解决办法] 建议看看这篇文章 http://cppgjd.wave12.com/ReadDoc/?20131314451626114422613676529[解决办法] 很遗憾,根据c++98, 03,std::string不保证memory和raw array一定一样 所以要直接和fread互动可能会出现一些很奇怪的问题(undefined behavior--讨厌死了) 可以先用fread读到std::vector<char>上再copy到std::string上 从stack overflow上抓来的其中一个答案,可以参考一下 string readFile2(const string &fileName) { ifstream ifs(fileName.c_str(), ios::in [解决办法] ios::binary [解决办法] ios::ate); ifstream::pos_type fileSize = ifs.tellg(); ifs.seekg(0, ios::beg); vector<char> bytes(fileSize); ifs.read(&bytes[0], fileSize); return string(&bytes[0], fileSize); }[解决办法] 引用: 很遗憾,根据c++98, 03,std::string不保证memory和raw array一定一样 所以要直接和fread互动可能会出现一些很奇怪的问题(undefined behavior--讨厌死了) 可以先用fread读到std::vector<char>上再copy到std::string上 从stack overflow上抓来的其中一个答案,可以…… string说到底还是一个字符串,始终是把'\0'当结束符看的,存储数据流不合适[解决办法] 引用: 好吧,我又想说老生常谈的KISS原则了。 反正就是解耦了,多组合少继承。尽量还是一个函数一个功能,我以前总担心明明一个函数可以做两个功能虽然会杂糅一点,但是比拆开性能高一点,后来才体会到性能瓶颈一般都不是自以为的地方,差那么点点也无所谓啦,逻辑简单了,耦合低,实在不行大不了再重构整合嘛 是的,这个函数做了很多事,读取文件的长度、开辟内存、拷贝内存,反正是不符合kiss原则啦。 做出这种设计,往往需要某些限制,比如类似strcpy事先传递已分配内存的指针,类似strdup调用后还需手动释放内存等。反正是给人一种很不优雅的感觉。 这个功能很类似mmap,可以参考下mmap的设计,比如你可以这样写(纯C写法,C++的话用类,并不会带来性能上的多大损失): 头文件中: struct _FileBuffer; typedef struct _FileBuffer FileBuffer; FileBuffer *FileBuffer_create(const char* filename, size_t offset, size_t size); void* FileBuffer_data(FileBuffer* thiz); size_t FileBuffer_length(FileBuffer* thiz); void FileBuffer_destroy(FileBuffer* thiz); int FileBuffer_exist(const char* filename); 实现文件中: struct _FileBuffer { int fd; void* data; size_t length; }; 再实现各个接口就可以了。 你可能会觉得这种设计很繁琐,但是好处也很明显。 1、它可作为一个通用的结构。 2、它提供了比你想象得多的功能,虽然现在你用不到,但好的设计就应该把基本功能做足。 3、提供了内存释放的接口。 4、易于扩展。