VC和GCC内成员函数指针实现的研究(一)
最近在《C++对象模型》一书里说到的virtual的成员函数指针,低于128的被cfront编译器认为是虚表偏移量(支持子类对父类函数的覆盖)。VC只是提了下单继承、多继承和虚继承的实现方案不同,GCC没有提及,所以就专门稍微深入分析研究下他们的实现机制。
最近在《C++对象模型》一书里说到的virtual的成员函数指针,低于128的被cfront编译器认为是虚表偏移量(支持子类对父类函数的覆盖)。VC只是提了下单继承、多继承和虚继承的实现方案不同,GCC没有提及,所以就专门稍微深入分析研究下他们的实现机制。
今天看到一个小例子,发现了一个小trick。见代码:
#include <cstdio>
#include <cstdlib>
class base_1
{
public:
int a;
};
class base_2
{
public:
int b;
};
class base_3: public base_1, public base_2
{
public:
int c;
};
int main(int argc, char* argv[]) {
printf("&base_1::a = %p\n", &base_1::a);
printf("&base_2::b = %p\n", &base_2::b);
printf("&base_3::a = %p\n", &base_3::a);
printf("&base_3::b = %p\n", &base_3::b);
printf("&base_3::c = %p\n", &base_3::c);
base_3 t;
t.a = 1;
t.b = 2;
t.c = 3;
typedef int (base_3::*tip);
tip pm = NULL;
printf("base_3::a = %d\n", t.base_3::a);
printf("base_3::b = %d\n", t.base_3::b);
printf("base_3::c = %d\n", t.base_3::c);
pm = &base_3::a;
printf("base_3::a(%p) = %d(ptr)\n", pm, t.*pm);
pm = &base_3::b;
printf("base_3::b(%p) = %d(ptr)\n", pm, t.*pm);
pm = &base_3::c;
printf("base_3::c(%p) = %d(ptr)\n", pm, t.*pm);
return 0;
}
猜猜看这个代码输出什么? 答案是:
最近看了glibc的ptmaoolc,Goolge的tcmalloc和jemalloc,顺便做了一点记录。可能有些地方理解地不太对,如有发现还请大神指出。
今天心情好,刷了两到ACM水题,思路很简单都在注释里,所以直接贴代码:
/**
* @file 龟兔赛跑.cpp
* @brief 龟兔赛跑 AC代码 (DP)
* DP方程式: [到第i的充电站的最短时间] = [到最后一个冲了电的充电站的最短时间] + [那个充电站到第i个充电站的时间]
*
* @link http://acm.hdu.edu.cn/showproblem.php?pid=2059
* @version 1.0
* @author OWenT
* @date 2013.07.15
*
* @history
*
*
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <numeric>
int pn[128];
double dp[128]; /** dp[i] 表示 到第i个充电站的最小时间(0为开始位置,n+1为终点) **/
double calc_charge_time(int dis, int v1, int v2, int c) {
if (dis <= c)
return 1.0 * dis / v1;
return 1.0 * c / v1 + 1.0 * (dis - c) / v2;
}
int main(int argc, char* argv[]) {
using namespace std;
double eps = std::numeric_limits<double>::epsilon();
int l, n, c, t, vr, v1, v2;
while(cin>> l) {
cin >> n>> c>> t>> vr>> v1>> v2;
pn[0] = 0; /** 0为起点 **/
for (int i = 1; i <= n; ++ i) {
cin>> pn[i];
}
pn[n + 1] = l; /** n+1为终点 **/
memset(dp, 0, sizeof(dp));
for(int i = 0; i <= n + 1; ++ i) {
dp[i] = calc_charge_time(pn[i] - pn[0], v1, v2, c);
}
for(int i = 1; i <= n + 1; ++ i) {
for(int j = 0; j < i; ++ j) {
double tc = calc_charge_time(pn[i] - pn[j], v1, v2, c) + t + dp[j];
dp[i] = std::min(tc, dp[i]);
}
}
double rt = 1.0 * l / vr, tt = dp[n + 1];
if (tt < rt)
puts("What a pity rabbit!");
else
puts("Good job,rabbit!");
}
return 0;
}
/**
* @file Zipper.cpp
* @brief Zipper AC代码 (DP)
* @link http://poj.org/problem?id=2192
* DP方程式: [A串消耗个数i][B串消耗个数j] = min{[A串消耗个数i - 1][B串消耗个数j]|[A串消耗个数i][B串消耗个数j + 1]}
* 以上分支选取条件是 A或B的新选用字符和C串新字符匹配
*
* @version 1.0
* @author OWenT
* @date 2013.07.15
*
* @history
*
*
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <numeric>
char strA[256], strB[256], strC[512];
int dp[256][256]; /** dp[i][j] = k 表示A消耗了i个字符,B消耗了j个字符,拼成的C串消耗了k个字符(其实就是i+j) **/
int main(int argc, char* argv[]) {
using namespace std;
int s, n;
scanf("%d", &n);
for( s = 0; s < n; ++ s) {
scanf("%s %s %s", strA, strB, strC);
int lenA = strlen(strA);
int lenB = strlen(strB);
int lenC = strlen(strC);
bool bFlag = false;
if ( lenC != lenA + lenB ) {
printf("Data set %d: no\n", s + 1);
continue;
}
memset(dp, 0, sizeof(dp));
if (strA[0] == strC[0])
dp[1][0] = 1;
if (strB[0] == strC[0])
dp[0][1] = 1;
for (int i = 0; i <= lenA; ++ i) {
for (int j = 0; j <= lenB; ++ j) {
if (0 == dp[i][j])
continue;
int ri = i + 1, rj = j + 1;
if (strA[i] == strC[dp[i][j]]) {
dp[ri][j] = dp[i][j] + 1;
if (ri + j == lenC)
bFlag = true;
}
if (strB[j] == strC[dp[i][j]]) {
dp[i][rj] = dp[i][j] + 1;
if (i + rj == lenC)
bFlag = true;
}
}
}
printf("Data set %d: %s\n", s + 1, bFlag? "yes": "no");
}
return 0;
}
最近看了点typescript的东西,加上以前看过的一点点Node.js,所以就想把他们系统地整理一下。
这玩意搞过Web开发的应该都知道吧,Javascript的语法我就不废话了,挺简单的。这里总结几个Javascript的核心机制部分吧。
慢慢一点一点看看Boost,这段时间就Asio库吧。 据说这货和libevent的效率差不多,但是Boost的平台兼容性,你懂得。还有它帮忙干掉了很多线程安全和线程分发的事情。
心情大好,给VPS升级了一下系统,然后自己配了LNMP安装脚本,用yum源安装的话更新比较方便点哈 这个过程挺麻烦啊,所以果断要记下来,以防以后要用到 如果是其他系统的话,几个配置路径和软件源地址还有yum指令替换掉,应该就可以了
使用代码生成代码是一件十分美妙的事情,于是有了各种代码生成器。但是生成代码,意味着要有对生成规则的分析和处理。 Boost.Spirit 就是这么一个语法分析工具,它实现了对上下文无关文法的LL分析。支持EBNF(扩展巴科斯范式)。 Boost.Spirit 的使用真的是把模板嵌套用到了极致。确实这么做造成了非常强的扩展性,生成的代码也非常高效,但是嵌套的太复杂了,对于初学者而言真心难看懂。 你能想象在学习阶段一个不是太明白的错误导致编译器报出的几十层模板嵌套错误信息的感受吗?而且,这么复杂的模板嵌套还直接导致了编译速度的巨慢无比。 其实在之前,我已经使用过Spirit的Classic版本,即1.X版本,但是过多的复制操作让我觉得当时用得很低效,还好分析的内容并不复杂所以没。体现出来 这回就来研究下功能更强劲的2.X 版本。
C++确实是一门复杂的语言。包括之前查看了一些C++11的文档和做了一些实践和总结,越来越觉得C++是门神奇的语言,也是个陷阱多多的语言。 我现在开发过程中最主要使用的语言就是C++,所以了解C++的一些细节和问题非常重要,后来看到某大神的一篇文章《C++的坑多吗?》,激起了我专门去看一看关于C++的一些常见的设计方法和问题的书。就是刚才提到的文章里有说的《Effecitve C++》和《More Effecitve C++》 共90个条款,所以说是90个坑。
C++11的新标准已经出台,各个编译器已经开始陆续支持。 主流编译器支持程度见(VC++, gcc, clang, intel c++等):http://en.cppreference.com/w/cpp/compiler_support
但是要让C++11应用与生产环境还需时日,所以就在这里记录一下在过渡时期可能用到的一些重要功能
终于要离开学校了,终于有时间可以静下来看看之前导师推荐的书籍。之前有看到说《程序员修炼之道》是对程序员影响最为深刻的书, 就从它开始吧。用这个还算可以的音响听着音乐,看书很惬意啊。 顺便吐槽下京东,我买了本地有货的三本书,三天了我还没见到。这效率实在是fuck。 第一本书的第一章是电子版上看的,还好我有kindle。这里基本上说的是沟通方面的。我发现我的沟通确实有点问题,不太主动,表达含糊。之前只和ultramanhu交流比较多,可能是多年的默契吧,表达清楚意思不怎么费劲。现在的一起合租的xboy(和qboy很像啊),和他交流经常文不对题,开始我总以为他习惯岔开话题,但是后来发现在其他有些人身上也出现过这种问题。看来我的表达力确实有问题,一直说ben大神的表达力低下,其实他只是我这种更恶化一些罢了。不管怎么说,之前看到过个视频,我觉得很有道理,对世界的理解应该是 “知其然 — 知其所以然 – 知其知其所以然 – 知其知其所以然所以然”。别人也是属于世界的一部分,了解别人看待事物和自己不一样、了解别人看待事物的角度、了解别人为什么和自己看待事物的和自己不一样,都是自身对这个世界的理解。同样,自己要表述的意思让各种各种各样的人有理解并且有兴趣听也是自身表达能力的一种体现。Maybe,这就是我大学生活孑然一身(不完整啊)的原因吧。T_T “注重实效的哲学”,其中最重要的部分当属那个WISDOM离合诗了吧。
这是我对C++新特性系统学习的最后一部分,之后就靠实践中再来看新标准的新特性啦。
在之前,我对这部分没太在意,直到看到了一篇文章 [http://blog.csdn.net/pongba/article/details/1659952](http://blog.csdn.net/pongba/article/details/1659952) 才意识到,C++的多线程操作也是个麻烦的问题。
简而言之,C++编译器在进行编译优化的时候,认为当前是单进程的,并且遵循**可观察行为**(Observable Behavior)不变的原则。就是说在可观察行为不变的情况下,操作是可以被改变顺序的,而单进程可观察行为不变,不代表在多进程的情况下仍然不变。还是上大牛的例子:
_**例子一:**_
完全可以优化成
C++在效率上有个硬伤。我们知道C#和Java对于类传递都是以引用的方式,而C++默认都是传值。在传值过程中就经常会进行复制构造,这完全没必要而且浪费CPU,为了解决这种问题,于是乎C++11 增加了一个新的非常数引用(reference)类型,称为右值引用(R-value reference)。我就专门看了一下关于右值引用的东西。 右值引用在GCC 4.3之后开始支持,VS 2010(VC 10.0)已经支持,再前一点的VC版本没试过所以不知道。 右值引用的申明标记为T &&,主要用于处理临时变量,比如函数返回的变量(暂时想不出其他例子,忽略返回值优化吧,(命名)返回值优化参见http://efnetcpp.org/wiki/Return_value_optimization,再说返回值优化能力有限是吧,比要求如单返回语句、不能使用异常等等),避免复制构造。同时在析构的时候就不会析构这个临时变量,从而提升效率。 上代码:
之前用Google的Protobuf感觉真是个很好用的东西,于是抽时间研究了下他的数据的存储方式,以后可以扩展其他语言的解析器。其实与其说是研究,不如说是翻译。这些文档里都有,可能有些地方理解的不太对,还请见谅。
详见: Linux编译安装GCC 4.7
系统: