免费注册 查看新帖 |

Chinaunix

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

C++primer这本书到底怎么样啊? [复制链接]

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
21 [报告]
发表于 2012-02-11 11:33 |只看该作者
回复 20# 三月廿七


    其实我感触倒没有这么深,就是觉得OO的接口很“脏”,我数一下吧:

- public函数
- private函数(注意还和C的static函数不一样,本质上也是public的,只不过语法不让你调而已)
- 成员(说白了就是结构体了,也是语法不允许你用,实际上还是public的)
- 各种重载(名字很乱不说,还没移植性,而且出了名的混乱)
- 各种重载运算符(把类搞得一塌糊涂的罪魁祸首:构造函数、析构函数、拷贝构造、赋值函数。是个类就得搞这四个,无聊啊)
- 继承(大负担,一堆细节)

相比起来模板反而还好些了 = =

反正结论是C++的接口比C的脏多了,各种东西混杂在一起,所谓的pimpl实际就是C模式的开发了,只是换个写法而已。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
22 [报告]
发表于 2012-02-11 19:56 |只看该作者
回复 21# starwing83

怎么前面还说得好好的,是OO的接口脏…… 后面就变成C++的接口脏了呢……
C++提供描述接口的方式多,也就容易被玩坏……
如果只提供黑白两色,再乱搞也只能搞出灰度图像;而C++提供的还不只是三原色,乱搞的程度就不可想象了……
不过无论是黑白两色还是三原色,都有各自的优势与劣势就是了。


你说的这些,有一部分应该是属于接口在源代码层面很好,但在二进制层面脏。
解决办法是将代码编译到用户程序中……

1. 重载函数

(先举几个例子,后面写头文件组织)

  1. R load(char const* file, size_t len); /* 以C接口提供实现 */
  2. inline R load(std::string const& file) { return load(filen.c_str(), file.length() ); }
复制代码

  1. struct X;
  2. struct Y;
  3. void f_X(struct X* x);
  4. void f_Y(struct Y* y);

  5. inline void f(X* x) { return f_X(x); }
  6. inline void f(Y* y) { return f_Y(y); }
复制代码
不仅仅是避免f_type,或者type_f这样的无数的名字,更重要的是为用户提供进一步的多态操作提供可能。

至少在C++里,就可以在模板中发挥多态能力了:

  1. template<typename T>
  2. void algo(T v) { f(v); g(v); }
  3. // 返回值有一些问题是因为C++被设计为重载不依靠返回值
  4. // 如果 f_X,f_Y有返回值也是可以弄的,只是演示代码就复杂了
复制代码
如果是f_X/f_Y/g_X/g_Y上面就没法写。

如果语言设计的好,应该也可以在尽量少写甚至不写代码的情况下立即使用类似virtual那样的多态。
只是C++的这种多态要靠class与继承才能完成……

头文件组织就类似于:

  1. #pragma once //为了短才写的这个,应该用#ifndef #define #else 的……

  2. #ifdef __cplusplus
  3. extern "C" {
  4. #endif

  5. R load(char const* file,size_t len);
  6. struct X;
  7. struct Y;
  8. void f_X(struct X* x);
  9. void f_Y(struct Y* y);

  10. #ifdef __cplusplus
  11. }
  12. inline R load(std::string const& file) { return load(file.c_str(),file.length() ); }
  13. inline void f(X* x) { return f_X(x); }
  14. inline void f(Y* y) { return f_Y(y); }
  15. #endif
复制代码
重载名字是乱,std:: string布局确实未知;但反正与用户是二进制一致的,就没有移植性问题。


2. 类型相关函数

C语言没有template的机制,就只好:
1. 用宏来产生代码
2. 用void*来抹掉类型

宏产生代码就不写了…… 倒不是C++复杂,而是宏复杂…… 最终都是编译到用户代码里。
抹掉类型(包括const修饰等),比如qsort,bsearch之类……
依然是通过C接口提供bsearch:

  1. void * bsearch ( const void * key, const void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) );
复制代码
类型不在, 而且const进non const出……


  1. // non-const版本
  2. template<typename T>
  3. T* bsearch(T const* k, T* p, size_t n, size_t s)
  4. {
  5.       struct nested {
  6.             static int cmp(void const* a, void const* b) {
  7.                   // cmp在这里重新获取类型
  8.                   T const& x = *static_cast<T const*>(a);
  9.                   T const& y = *static_cast<T const*>(b);
  10.                   return x<y? -1: ( y<x? 1: 0 ) ;
  11.             }
  12.       }; // C++11就不用这么麻烦了……
  13.       void* v = bsearch(k, p, n, s, &nested::cmp /*cmp消去类型*/);
  14.       return static_cast<T*>(v); // 恢复T*
  15. }

  16. // const版本
  17. template<typename T>
  18. T const* bsearch(T const* k, T const* p, size_t n, size_t s) {
  19.       T* v = const_cast<T*>(p); // 为了转发到上面那个版本
  20.       v = bsearch(k, v, n, s);
  21.       return v; // 恢复 const 限定
  22. }
复制代码
3. 析构函数

这绝对是好东西啊……  lua还求之不得呢……
其实需要的机制是能注册一些函数在离开作用域(尤其是非局部跳转时、异常,error,coroutine……)时调用。
其实lua的coroutine可以用来实现C++当初被砍掉的恢复语意的异常,缺的就是退出作用域时的保护机制……

只是C++被设计为通过栈上变量的析构函数来注册……
这样的设计可能不是最好的,但至少比没有要来得好吧……

如果你真不喜欢析构函数,还有loki.scopeguard, boost.block_exit什么的来帮你写。
同样是将需要的恢复步骤用C接口提供, 然后利用上面两种库帮你调用。


====== ====== ====== ======

总之, 你想要干净的C接口, C++肯定能做到。
而且还可以在这干净的接口上, 在用户端提供一些辅助。
就是我以前说的:

  1.            by C
  2. lib     <-------->  user A by C
  3. by C++      |
  4.             |---->  user B by C++ (lib by C++)
  5.             |
  6.             .....
复制代码
只是:
1. 很多人都不知道这种接口方式 —— 至少坛子里那些喜欢时不时通过贬低C++来获得优越感的人通常都不知道。
以前编译器可能不像现在这么多, 版本问题可能也没现在这么严重, 不太注重user/lib使用不同C++编译器会造成什么情况。
即使现在很多人都不知道,或者知道了也不关心这个问题, 只有你我这种有洁癖的才会……

2. 大部分程序员都被OO玩坏了
C++一开始是从C with class发展起来的, 只有那些个描述接口的方式…… 只有这些东西可以让新手模仿……
但不是人人都能认识到自己模仿的东西的缺点, 特别是在没有比较的情况下。
而且, 这玩意需要的智商确实低, 玩得转, 连非编程人员都可以将面向对象当作谈资

3. 即使知道这种接口方式, 实现起来也是很麻烦的……

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
23 [报告]
发表于 2012-02-11 20:02 |只看该作者
回复 20# 三月廿七

进步神速啊……
我从研究设计模式到认识到这玩意是个渣,可能花了半年到一年的时间……
单件那个帖子到现在可能才2-3个月吧……

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
24 [报告]
发表于 2012-02-11 20:11 |只看该作者
回复 21# starwing83

关于构造/析构/拷贝/赋值说漏了……

lua没有这个烦恼是因为:
1. 它没有访问控制(不要提闭包……)
2. 它没有定义新类型的能力(不要提元表……)
3. 它有gc

类似的,C++如果只用tuple而非定义新类型,同样不需要关心那4个函数,以及public/private什么的。

论坛徽章:
0
25 [报告]
发表于 2012-02-11 20:22 |只看该作者
OwnWaterloo 发表于 2012-02-11 20:02
回复 20# 三月廿七

进步神速啊……


你的水平我望尘莫及.

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
26 [报告]
发表于 2012-02-11 22:09 |只看该作者
本帖最后由 starwing83 于 2012-02-11 22:10 编辑

回复 24# OwnWaterloo


    很好,我就一起回了。这些的确没往这些方面想过。很好,不过貌似是C接口+C++ wrapper,只是写到了一个文件而已,是吧?其实如果要接口干净还可以C++ + C wrapper,实际上要接口干净C接口是少不掉的,当然自己能控制名字这点挺好的。

然后lua没有是因为它是动态语言……你懂的。闭包的确提供了访问控制哈,元表的确产生了不同行为哈,基于table的对象的确提供了virtual的能力哈,这个有啥好说的捏,最多就是gc惹的祸嘛,大家都不gc了,很多代码就不好写了= =有gc没析构,有析构没gc,你咋想?

如果lua声明,离开作用域的变量立刻gc,是不是好一点?

或者添加一个with语句?

你觉得什么解决方案会比较好?


PS:关于异常时的资源释放,你误会lua了, 其实lua支持异常时强制回收所有资源的,只需要显式完全gc即可,这点其他动态语言没一个暴露接口的,tolua++生成的代码这么干了(error之前完全gc)

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
27 [报告]
发表于 2012-02-11 23:33 |只看该作者
starwing83 发表于 2012-02-11 22:09
很好,我就一起回了。这些的确没往这些方面想过。很好,不过貌似是C接口+C++ wrapper,只是写到了一个文件而已,是吧?其实如果要接口干净还可以C++ + C wrapper,实际上要接口干净C接口是少不掉的,当然自己能控制名字这点挺好的。

1. C++ + C wrapper 写起来应该比 C + C++ wrapper更难……
2. 其实最终结果应该都差不多,关键是写代码的人的意图是怎样的
3. 意图主要是写库给其他C代码用, 可能就会更少关注 C++ wrapper 甚至根本是由第3放开发的……
4. 我说的这种意图主要还是写给其他C++代码使用
而这么绕弯子主要还是为了避免lib/user的编译器之间的耦合;其次这样确实可以避免接口很脏, 因为实质上二进制接口是C的。
5. 剩下的就是C++写给C++用,但不考虑这些乱七八糟的东西, 只管产生乱七八糟的接口就是了……


starwing83 发表于 2012-02-11 22:09
然后lua没有是因为它是动态语言……你懂的。闭包的确提供了访问控制哈,元表的确产生了不同行为哈,基于table的对象的确提供了virtual的能力哈,这个有啥好说的捏,最多就是gc惹的祸嘛,大家都不gc了,很多代码就不好写了= =有gc没析构,有析构没gc,你咋想?

构造/析构/复制/赋值,我觉得与类型检测无关,是与是否可以定义新类型有关。
python/js也是动态类型检测,但它可以定义新类型,也就顺带一堆规则。
也许没这么复杂,比如可能没析构,这可能就与gc有关了。


starwing83 发表于 2012-02-11 22:09
大家都不gc了,很多代码就不好写了= =有gc没析构,有析构没gc,你咋想?
如果lua声明,离开作用域的变量立刻gc,是不是好一点?
或者添加一个with语句?
你觉得什么解决方案会比较好?
PS:关于异常时的资源释放,你误会lua了, 其实lua支持异常时强制回收所有资源的,只需要显式完全gc即可,这点其他动态语言没一个暴露接口的,tolua++生成的代码这么干了(error之前完全gc)


1. gc这个东西,无论自己怎么抵触,但确实没有它代码写起很累……
2. 看来C++的raii对其他语言/用户还是有潜移默化的影响……
以前幻之上帝也提到这个问题……
让对象持有资源只是C++恰好这么做……

最终结论反正在qq上也有了, lua提供的机制完全可以实现恢复语意的异常, 只是非标准化, 而是idiom, 需要其他代码(包括C)也遵守才行。

论坛徽章:
0
28 [报告]
发表于 2012-02-12 17:10 |只看该作者
回复 21# starwing83

c++ 的名称起的真是名副其实
如果让我用 c 去继承,一点难度都没有,也就是手工调一下destroy()函数
c++ 那种填鸭式的语法,我真招架不住

你能列举一下 继承都有哪些细节吗?

   

论坛徽章:
0
29 [报告]
发表于 2012-02-12 17:21 |只看该作者
有一部分情况下 深度复制、浅度复制、复制构造、赋值函数 这些都用不到,
因为没有 复制、赋值 这些操作.

论坛徽章:
0
30 [报告]
发表于 2012-02-12 17:23 |只看该作者
c++ 做的真过头了,搞的我心烦意乱的,
这东西复杂起来真可怕
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP