忠忠的新百宝袋

做人要厚道

Archive for the 'c++' Category


毕设过程8——更好的光照

Posted by Kai on 18th April 2008

光照使用了负责的Phong模型(其实我认为Phong模型和Gouraud模型的差别是计算方法不是光照模型)。另外使用更另一种新的比较自然的颜色,得到的效果也提升了很多。

 

cadaver-Data(4-18-2008)_Time(16-7-25)_968

光照模型里带有高光参数,很多地方只是随便的设个参数,看这还可以就过了。

我考虑我的光线合成或者模糊运算的方法可能有错,因为在原本黄色的颜色上添加了一些白色的条条。

我还顺便添加了根据计算机的配置自动选择最合适的分辨率的功能,其实很简单,就是二分搜索到我需要的FPS为止,也好快,如果设FPS目标是10的话,大概1秒不到就能搞找到合适的分辨率。

Posted in c++, tech | No Comments »

C++的有趣而罕见的特性:局部类

Posted by Kai on 14th April 2008

昨天看《C++设计新思维》,看到C++有个很有趣但是很少有人用的新特性,局部类。

就是可以在函数里面定义一个类,这个类在函数外面无法访问到。

这样,配合模板就可以在常用的循环里面很方便的做出for_each循环的效果。

因为如果使用自己定义的数据结构,要使用for_each循环就得自己定义迭代子,而且在迭代器模式中,尽管有迭代子的帮助,共有的循环控制代码还是重复了。

比如:

iterator it = vector::begin();
while(it != vector::end())
{
    //do something
   it = it->next;
}

在这种模式下,对容器的遍历被抽象为迭代子的步进,但是如果需要多个处理,while循环还是要写很多次。

现有的一个方法是使用std::for_each模板,通过

std::for_each(vector::begin(), vector::end(), DoSomethingClass());

这样需要建立一个DoSomethingClass的类。

可惜C++不像Java和C#一样有匿名类的机制,那么一般的解决方法就是在外部建立一个DoSomethingClass的类。然而这样处理就向外界暴露了这个类,使得程序接口更加复杂。

在我的程序中,有这样一个功能:

for(int x = 0 ; x < x_Max; ++x)
{
    for(int y = 0 ; y < y_Max; ++y)
    {
        for(int z = 0 ; z < z_Max; ++z)
        {
            DoSomeThing();
        }
    }
    ShowProceedBar();
}
Refrush();

在好几个函数中我都有这种循环,唯一的区别就是DoSomeThingA(), DoSomeThingB(),…,这样三重循环的代码就被重复了好几次。重复是代码罪恶的源头。

而对于一个三重循环,要实现stl的iterator接口可能有点复杂。

现在可以用一个漂亮的解决方法。

首先建立一个模板函数:

template<typename Functor>
void DoEach(Functor func)
{
    for(int x = 0 ; x < x_Max; ++x)
    {
        for(int y = 0 ; y < y_Max; ++y)
        {
            for(int z = 0 ; z < z_Max; ++z)
            {
                func();
            }
        }
        ShowProceedBar();
    }
    Refrush();
}

然后根据我的需要,在原来的诸多函数的基础上,比如说:

void DoA( for…for…for (…DoSomeThingA())… );
void DoB( for…for…for (…DoSomeThingB())… );
void DoC( for…for…for (…DoSomeThingC())… );
void DoD( for…for…for (…DoSomeThingD())… );

在DoX()函数里面建立局部类,把原来的操作代码封装到局部类的operator()的定义里面去。因为局部类只在函数内部有效,所以不用担心名字冲突的问题。像这样:

void DoA()
{
    class Func
    {
    public:
        void operator()()
            {
                DoSomeThingA();
            }
    };
    DoEach(Func());
}

void DoB()
{
    class Func
    {
    public:
        void operator()()
            {
                DoSomeThingB();
            }
    };
    DoEach(Func());
}

因为class定义是在编译时处理,所以DoX在运行时实际的代码只有

void DoA()
{
    DoEach(Func());
}
void DoB()
{
    DoEach(Func());
}

 

相当于在外部定义了DoX_Functor。但是,得益于局部类的名称局部性控制,使得对与调用DoX函数的外部代码,DoX_Functor完全透明。

新的代码比原来的代码优雅,因为邪恶的重复循环代码只有一处。另外,对于DoX系列函数的调用者,什么都没有改变。可以认为我只是在函数内部做了调整,以达到减少重复的目的。实际上,在我的程序里,DoEach模板是私有成员模板函数,类的接口没有变化。

———————————————————————————–

刚刚在修改我的程序的时候发现了更酷更漂亮的做法,那就是搭配使用宏,来简化一些琐碎的工作。

#define For__Each(code); class Func \
        { \
        public: \
                void operator()() \
                { \
                        code \
                } \
        }; \
        DoEach(Func());

然后在DoA里面,只要写
void DoA()
{
        For__Each(
                DoSomeThingA();
        );
}
就可以了。

Posted in c++, tech | 1 Comment »

一个生成代码的小练习

Posted by Kai on 11th April 2008

在《程序员修炼之道》里面看到了这个题目:

Exercises 13:

Generating code from a language-neutral representation. In the input file, lines starting with ‘M’ flag the start of a message definition, ‘F’ lines define fields, and ‘E’ is the end of the message.

# Add a Product
# to the ‘no-order’ list
M AddProduct
F id    int
F name    char[30]
F order_code int
E

Generate C:

typedef struct {
    int    id;
    char    name[30];
    int    order_code;
} AddProductMsg;

Generate Pascal:

{ Add a Product }
{ to the ‘no-order’ list }
AddProductMsg = packed record
    id:        LongInt;
    name:        array [0..29] of char;
    order_code:        LongInt;
end;

今天我花了接近3个小时做这个程序,顺便练习一下最近学的设计模式。

下面是我的类图:

TargetFamily

这是输出用的类,其中使用了模板方法,工厂方法模式。

TypeFamily

这是字段的类型类,因为数组类型和原子类型输出不同,所以分开,这里也许应该用访问者模式。

CallSequence

这是调用顺序图,Type和Meta都必须通过工厂TypeResolver和MetaResolver产生实例,Type的虚礼析构函数向堆中释放自身。

其中我不满意的地方有:

1. 使用了protected访问控制,在Type的构造函数里面。protected是丑陋的。

2. 使用了friend class机制,以让工厂类能够访问MetaStrut和AtomType,ArrayType。friend机制是丑陋的。如果我做另外一个什么DerivedType,我必须在这个DerivedType里面再声明friend class,这就使得不能完全解耦。

第一个,如果Type不允许实例化的话,我也可以把Type的函数做成纯虚函数,这样就不需要使用protected保护Type了。

第二个,我没想到好的解决办法。我不能让MetaStrut、AtomType和ArrayType可以在栈上被实例化。为了使用多态,简单工厂返回的是基类的指针,所以不可以使用栈对象。使用堆对象的话,又必须在析构函数里面调用delete this。从而就不能有栈对象的存在,为了防止其他函数使用建立栈对象,我就想到把构造函数设成Private。这样建立对象的唯一机会就是在简单工厂里面,可以保证都是堆对象。

一下是我的程序的输出:

/* Add a Product */
/* to the ‘no-order’ list */
typedef struct {
    int    id;
    char    name[30];
    int    order_code;
} AddProductMsg;

{ Add a Product }
{ to the ‘no-order’ list }
AddProductMsg = packed record
    id:        LongInt;
    name:        array [0..29] of char;
    order_code:        LongInt;
end;

// Add a Product
// to the ‘no-order’ list
class AddProductMsg
{
public:
    int    id;
    char    name[30];
    int    order_code;
};

PS:这次做程序的过程中,我使用了Refactor!Pro来重构代码,使用了Extract Method,感觉很不错,这次的代码我看着也觉得有点漂亮。可惜我的Refactor!Pro是体验版,还有10天就到期了。另外,我决定以后一定要学会用flex,自己处理字符串既费时又没有意义。

Posted in c++, tech | No Comments »

Protected and Private Access Level

Posted by Kai on 2nd April 2008

今天有个新发现,我以前对于protected的理解一直是错误的。看下面这个代码:

#include <iostream>

using namespace std;

template<typename T>
void writeline(T  t)
{
    cout<<t<<endl;
}

class A
{
public:
    A() { }
    A(A& a)
    {
        pub = a.pub;
        pri = a.pri;
        pro = a.pro;
    }
     void print()
    {
        writeline (pub);
        writeline (pri);
        writeline (pro);
    }

    int pub;
private:
    int pri;
protected:
    int pro;
};

class B: public A
{
public:
    B(A& a)
    {
        pub = a.pub;
        pri = a.pri;
        pro = a.pro;
    }
};

int main()
{
    writeline(sizeof(A));
    writeline(sizeof(B));

    A a;
    int * pub = (int*)&a;
    *pub = 1;
    int * pri = pub + 1;
    *pri = 2;
    int * pro = pub + 2;
    *pro = 3;
    a. print();

    B b(a);
    b.print ();
}

这段代码编译不通过,报3个错误。

1 error : ‘A::pri’ : cannot access private member declared in class ‘A’
2 error : ‘A::pri’ : cannot access private member declared in class ‘A’
3 error : ‘A::pro’ : cannot access protected member declared in class ‘A’

说明:

1. 为了保存内存布局一致,即使在子类中无法直接访问的字段在子类的对象中也存在,所以sizeof(A)和sizeof(B)同为12。编译器返回错误1,2。

2. protected可访问性对派生类而言只能在*this对象中使用。所示B的构造函数中,pro = a.pro里面,第二个pro访问出错。第一个没出错。编译器报告错误3。

以前在Effective C++里面看过,但是不明白的地方,就是为什么说protected最好不要使用。现在看来,protected还真有点鸡肋的感觉。唯一方法是通过调用父类的函数访问父类的字段,但是这样就和使用private一样。那还不如使用private来的简单。

Posted in c++, tech | No Comments »

好多东西要学

Posted by Kai on 23rd March 2008

最近看了几本书,发现有好多东西要学。

看《设计模式》,发现我过去写的代码真是垃圾,设计的一点复用性都没有。

看《游戏之旅-我的编程感悟》,发现我对MMX,SSE一窍不通。还有C++模板的使用,简直是智力题。

以前我错误的以为C++的模板,类相比C只是可以节省程序员代码量的编译时技巧,在看过设计模式之后才意识到如果不用真正的设计是要使用虚函数的设计,而虚函数的存在,是为了避免大段的switch_case,同时在《游戏之旅》中,作者提到虽然虚函数增加了一次间接取值的操作,可以因为虚函数给编译器提供了更多的信息,对于分支预测有帮助,效果也许不比switch_case大。ps:在C里面模拟虚函数的方法是使用函数指针数组,C++里面的虚函数更加高效。

Posted in c++ | No Comments »

Hello Android World

Posted by Kai on 28th February 2008

Hello World实在闲着没事,去下载了Android的SDK,刚刚看到了第一个HelloWorld的运行图,这一刻,我泪流满面。好复杂的东东……

Posted in android, c++, java, tech | No Comments »

VC下输出虚函数表

Posted by Kai on 21st October 2007

昨天摩根斯坦利笔试受了点刺激,回来小小研究了一下虚函数表。今天写了一个输出虚函数表的程序。经过测试,在VC2005下运行正确。而且取出的虚函数指针的确能用。

代码在 本帖阅读全文链接里。

最后还有一个问题没能解决:

  A a;

MemFun  p1 = &A::foo;
MemFun  p2 = ** ((MemFun**)&a );
printf("p1 = %pt p2 = %pn", p1, p2);
(a.*p1)();
(a.*p2)();

运行结果显示这段代码都是调用了A::foo函数,但是打印出来的p1和p2的值确不一样。难道A::foo函数有两个代码段?

#include "stdafx.h"
#include <iostream>

using namespace std;

class A
{
public:
virtual void foo(){ cout<<"foot"<<endl; }
int x;
};

class B: public A
{
public:
virtual void bar(){ cout<<"bart"<<endl; }
};

int main()
{
  A a; B b;
typedef void (A::*MemFun)() ;

cout<<"test a"<<endl;

MemFun * vpa = * ((MemFun**)&a );
//此时vpa是&A::foo
(a.**vpa)();

–vpa;
while(*vpa)
{
  printf("%pt%pn", vpa, *vpa);
  ++vpa;
}

cout<<"test b"<<endl;
MemFun * vpb = * ((MemFun**)&b );
//此时vpb是&b::foo 即 &A::foo
(b.**vpb)();

++vpb;
//此时vpb是&b::bar
(b.**vpb)();

–vpb;
–vpb;
while(*vpb)
{
  printf("%pt%pn", vpb, *vpb);
  ++vpb;
}
printf("&A::foo = %pn", &A::foo);
printf("&B::foo = %pn", &B::foo);
printf("&B::bar = %pn", &B::bar);

MemFun  p1 = &A::foo;
MemFun  p2 = ** ((MemFun**)&a );
printf("p1 = %pt p2 = %pn", p1, p2);
(a.*p1)();
(a.*p2)();

system("pause");
    return 0;
}

Posted in c++, personal, tech | No Comments »

刚刚编了个小程序从excell2007格式文档里读取我每个月的花钱记录

Posted by Kai on 15th October 2007

我从上学期开始每天记录我花了多少钱,每个月放在一个excel文档里,每天做一个sheet。这样到现在一共有3月到9月的全部花钱记录。

昨天晚上烦闷之极就把xlsx文件按照msdn上说的,重命名为zip,然后解压,我在里面一大堆文件中找到了xlworksheets路径,这里面sheet的数目和我文档里实际的sheet的数目差不多,我用ie打开其中一个xml文件,发现里面有一些关键字,比如sum,subtotal,后面的值正好是我那天消费额度的总数。所以我想这个文件夹大概就是保存所有数据信息的地方。

今天下午我正好不想上课,就写了这个c++程序。我要的信息并不是详细的分析记录,我就是想要看看我每个月花了多少钱,从上学期到现在一共花了多少钱。而且我也不知道C++里有没有xml的库函数,我也不可能自己现在去写一个xml解析程序。我就用最原始的关键字查找,首先查找sline.find("<c r="B1" s="2">");,因为我的所有数据都是记在同一张表格里面的,格式很工整,关键数据的位置都是一样的。正常我的数据表格B1格就是存放的日期。excel里面日期是按照1900年1月0日为起点,多一天加一的方式保存的。不过在我这个通用性要求不高的地方,我只要从2007年1月1日开始算就可以了。

找到日期后,首先把日期转换成月-日二元组的格式,这个很容易。然后再查找sline.find("SUM"),不行就查找sline.find("SUBTOTAL"),下面的v属性里面就是当天的总额。我以前只知道atoi是字符串转整数,我今天猜了个atof函数,以为是字符串转浮点数,没想到竟然真的有这个函数,太方便了。

这样读取一个解压后的文件就成功了。我不知道怎么在C++里面解压zip,所以就用了一个shell脚本命令把所有.xlsx文件重命名为.zip文件(需要在环境变量里面把winrar的路径添加进去),然后写个命令把所有zip文件解压,后来我发现其实不需要重命名,我直接可以用winrar命令吧.xlsx文件加压,命令为:

for %i in (*.xlsx) do winrar x %i %~ni

得出的结果很漂亮,看出来我还是很节约的。3月份考托业和买教材花了不少钱,7,8月老师管饭,花的很少,8月份配了一个眼镜花了310,9月份去了上海面试一次花了200路费,10月份同样要去上海,另外请人吃饭,所以上半月也花了不少。

07年1月份消费总额:      0.000000
07年2月份消费总额:      669.400000
07年3月份消费总额:      1153.810000
07年4月份消费总额:      515.480000
07年5月份消费总额:      338.970000
07年6月份消费总额:      389.670000
07年7月份消费总额:      235.960000
07年8月份消费总额:      595.000000
07年9月份消费总额:      852.500000
07年10月份消费总额:     399.200000

**********************
07年消费总额:   5149.990000
Press any key to continue . . .

Posted in c++, personal, tech | No Comments »

看完了一些C++的书,C++基本复习了一遍

Posted by Kai on 21st August 2007

从8.1号开始看Exceptional C++ Style, 其后看 Effective C++, 接下来Exceptional C++, 再看C++ Primer, 今天把Primer看完了,发现了C++里面原来我还有很多地方不知道的。我大一时候学的真的实在是太基础太表面了。这是我的问题,学校的安排也有问题,教材完全是入门型。对与科班来说是远远不够的。不过我也仅仅是看完,很多示例没有时间做一遍。

9月份有空想看传说中的Inside C++ Object Model,如果图书馆不买的话就要自己掏钱去买了。上一次实习生招聘会的时候,直接被人家问看没看过Inside C++,我说没,他说这么经典的书都没看过,切。

昨天做glut中想插入MFC的代码的,结果出问题了。glut原来是个简化版的GUI库,我过多的期望glut了。想实现的功能,还得用MFC来做。

Posted in c++, tech | No Comments »

SURPRISE!在循环内部声明变量比在外部声明快

Posted by Kai on 15th August 2007

比如下面这个代码:

1) for(int i = 0 ; i < k ; ++i)
       {
           Matrix temp (ma1 + ma2);

  2)Matrix temp; 
       for(int i = 0 ; i < k ; ++i)
       {
             temp = ma1 + ma2;

我以前一直以为2更加高效,所以一直用第二种形式。今天我才知道,原来第一种才更加高效。惊奇吧,一方面,第一种程序里没有赋值操作,只有拷贝构造函数,第二种程序里有赋值操作。而赋值操作总是比初始化要慢的。另一方面,第一种程序的局部性更好,对缓存的利用率更佳。

实验的结果就是,第一种写法要快上10%.这真是个大大的surprise.

Posted in c++, tech | No Comments »

 
FireStats icon Powered by FireStats