博文
去掉C/C++源程序中的注释(2008-04-12 16:27:00)
摘要:一道题目,输入是一个正确的C/C++源程序,输出是将所有注释去掉之后的程序。不细想觉得很简单,字符串搜索,找到//后再找一个回车,删掉,找到/*后再找一个*/,删掉,还有什么好做的,太简单了。
给个测试例子:
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
cout<<"line 1 // hello"<<endl; // line 1
cout<<"line 2 /* haha~ */"<<endl; // line 2
cout<<"line 3 \" /* hehe */"<<endl; /* line 3 */
/* ********\\\\\\\\\\/////////********
this is test program
this is test program
this is test program
*/
return 0;
}
这是正确的C++程序,它的输出要是:
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
cout<<"line 1 // hello"<<endl;
cout<<"line 2 /* haha~ */"<<endl;
cout<<"line 3 \" /* hehe */"<<endl;
return 0;
}
哇,这么一来想想就觉得复杂,字符串,先要找到字符串,字符串里的不算,字符串里还有转义符。。。
用状态机做会不会很方便,输入集中比较特殊的就这几个:/,*,",\,关键是在它的构造,在本子上画个表,纵向是表示状态,横向表示......
TMP计算素数(2008-03-21 22:45:00)
摘要:这个程序不能通信编译,说明它并不是运行在运行期,它借助编译错误信息报出'指定范围的素数',比如下面程序将会在primes<x>中x是素数的类中发生错误,看错误信息吧...
#include <cstdlib>
#include <iostream>
using namespace std;
struct true_type
{
private:
true_type(){};
};
struct false_type{};
template<bool,typename,typename B>
struct IfThenElse
{
typedef B result_type;
};
template<typename A,typename B>
struct IfThenElse<true,A,B>
{
typedef A result_type;
};
template<int d,int i = d/2 >
struct is_prime
{
typedef typename IfThenElse<d%i==0,false_type,typename is_prime<d,i-1>::result_type>::result_type result_type;
};
template<int d>
struct is_prime<d,1>
{
typedef true_type result_type;......
[笔记].template 修饰 , 双重模板(2008-03-20 22:38:00)
摘要:代码:
template<int N>
void printBitset(std::bitset<N> const& bs)
{
std::cout<<bs.template to_string<char,char_traits<char>,allocator<char> >();
}
能在DevCPP里编译通过,那个.template的意思就是告诉编译器,紧跟to_string的<并不是一个小于号,而是模板参数;需要这个 template修饰的地方(.或者->)是当前正处在一个模板中,前面的bs还未具现,它是一个模板函数里的某个参数,所以编译器是不知道它是什么东西的,如果你具现了,比如std::bitset<100> bs;那就不需要再加.template了。这和typename的第二个重要用法是一样的,用以申明模板中的参数的内嵌类型是一个类型,而不是静态变量。
双重模板是在模板参数中再用模板,比如
template <typename T,template <typename> class CONT >
stack{...};
不是所有编译器都支持这个较后增加的C++特性,当然你需要在这个类里面形式化的具现这个模板模板参数,像CONT<T>...。......
我写的多维数组auto_array[续](2008-02-19 19:00:00)
摘要:实现生成器,语法更简洁:
shape是一个全局唯一对象,用来生成多维数组的'外观',可以拿传统的C语法进行比较:
定义一个三维数组(传统):
int a[3][4][5];
一个动态三维数组:
auto_array<int,3> a(shape[3][4][5]);
形式上很相似,如果你不进行特殊的操作,完全可以拿下面的a代替上面的.所不同的是,传统的[3][4][5]是需要在编译期确定的,而下面的可以是运行期的动态变量,所在的存储空间也不同,上面占用栈空间,下面的是在堆上,由指定的分配器优化分配的.
你如果随意乱用shape,比如
auto_array<int,3> a(shape[3][4]);
将会引发编译错误,同样的,对其它维度数和shape后面的[]数不同的形式都会报错.换句话说,shape的[]会返回不同的类型,这取决于你用了几次[].
shape还可以用来传给reshape,形式是
a.reshape(shape[3][4][5]);
它不能再继续加括号指定范围了,他不能指定起始下标,如果希望不从0开始下标数组,只能使用另一种形式的reshape,这种形式值得再说明一下.
reshape(dim0,width0,start0)(dim1,width1,start1)(...)...;
为了防止空间的重复无用的分配,这里的reshape,在调用operator()的时候,并没有真的去'分配'它,只是设定了相应的维度宽度等值.而需要真正分配空间,可以几种方法:
1,调用一次operator[],哪怕没任何用.
2,在reshape语句末尾,加上一句now(),如
a.reshape(0,4)(1,5).now();
则会马上发生分配.有没有真的分配,会影响到其它和容器相关的函数,包括:
safe_bool转型,可以这样使用:
if(a)
cout<<"a is not empty!"<<endl;
a可以自动转型,但这是安全的,你不能指望这样的代码通过编译:
bool b=a;
或者其它形式的头疼地隐式转换:
a && true;
[这种安全转型是从boost里学的;)]
此外还有一些标准的迭代器函数,begin(),en......
我写的多维数组auto_array(2008-02-19 00:08:00)
摘要:练手的
[定义]
auto_array<typename T, std::size_t dim, typename Allocator = _test_allocator<T> > a;
第一个参数是元素类型,第二个是最高维度,维度标记从0到dim-1,第三个是一个分配器
[重塑数组大小]
reshape(std::size_t dim_index, std::size_t width, long start = 0);
第一个参数是指定第几维,第二个参数是指定该维的宽度,第三个参数是起始下标. 这个函数可以重叠使用,如
a.reshape(0,4);
a.reshape(1,5);
等价于
a.reshape(0,4)(1,5);
[operator[]操作]
和C语言的数组[]操作一样,没有任何不同,只是多了运行时越界检查.
测试代码(编译环境Dev C++(GCC)):
#include <iostream>
#include "auto_array.hpp"
using namespace std;
int main(int argc, char *argv[])
{
auto_array<int,1> a;
a.reshape(0,5);
try {
a[0]=1;
a[0]+=2;
a[1]=3;
a[4]=a[0]*a[1];
}
catch(runtime_error &err)
{
cout<<err.what()<<endl;
system("PA......
[笔记]enable_if的原理(2008-02-16 02:34:00)
摘要:enable_if是用来(可以用来)控制重载函数的是否可用,这一点没有直观感受,比如,我写了一个形式的重载函数,但是我需要在某种条件下取消它,这时候它就可以派上用场,当然这里是模板函数.
function的构造函数有类似下面的形式:
template<typename Functor>
BOOST_FUNCTION_FUNCTION(Functor BOOST_FUNCTION_TARGET_FIX(const &) f
#ifndef BOOST_NO_SFINAE
,typename enable_if_c<
(boost::type_traits::ice_not<
(is_integral<Functor>::value)>::value),
&n......
boost::assign让容器赋值更简单(2008-02-05 16:01:00)
摘要:分析源码其实很有意思,尤其是优秀的源码。boost::assign库,老实说,设计初衷有些无聊,它无非是想让容器的赋值(插入)操作更加简单,简单得就像脚本语言一样,比如,原来的STL里的容器有各种各样的,不同的容器会有不同的赋值方式,比如:
#include<vector>
using namespace std:
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
return 0;
}
如果你不觉得敲‘.push_back’多么麻烦,大可不必管这个库,实际上这样写,机器看起来更简单。而boost就想办法让人偷懒,而机器转一些弯,成了这样:
int main()
{
vector<int> v;
v+=1,2,3;
return 0;
}
能够运行的示范代码我没有找到啊,只是写个样子出来,OMG,真像脚本语言!你要问,效率如何?可能会牺牲一些,就像,我问你,仿函数比函数的调用效率是不是低一些,如果是,那STL里还不是大量用仿函数?但这些差别,绝对不是相差很大,不会有质的差别。
好了,下面进行分析,我不是设计者,不知道大哥他是怎么想的,我只从分析的角度说明。
1,设计各种仿函数来管理具体的容器和它的赋值方法
仿函数比函数好的一个地方,它可以有成员变量,而这里就关联了这个具体的容器。在源码的assign/list_inserter.hpp里的前面部分,有一个namespace assign_detail命名空间,在那里定义了各种,仿函数,比如调用push_back的
template< class C >
class call_push_back
{
C& c_;
......
boost::any实现一个容器里放多种类型的对象(2008-01-31 23:44:00)
摘要:STL里的容器要求只能是一种类型,比如
vector<int> v;
这个数组里就只能放int型数据,换了其它的数据,一不认识,二如果大了还放不下,当然,大多数情况不需要这样的功能,我要的就是一个int的数组,我疯了还往里面push一个浮点数?我也暂时没想到需要在一个容器里放多种类型数据的应用场合,至少这种要求也并不过分,就当是考验一下C++,它能做到吗?
boost::any库肯定的回答了这个问题,从名字上看,它能放下任何类型的数据,口气不小.可以这样定义:
using namespace boost;
vector<any> v;
v.push_back(any(100));
v.push_back(any(3.14));
boost的源码是免费的,下来打开看就知道了,整份源码只有200多行,很简洁很强大!它内嵌有一个基类placeholder,不做任何事,然后还有一个模板类holder<T>,它从placeholder派生下来,holder<T>是对T的一个封装,有一个成员就是T held,数据本身,然后在any类里面有一个placeholder的指针,基类指针可以指向任何子类成员,即holder<T>的对象,而holder<T>可以具现成任何类型T的一个holder - - (很简洁很强大吧)
画个图
placeholder指针->
任意类型的holder
holder<int>, holder<float> ...
这些都容易,但有一个问题,在需要一个容器放多种类型数据的时候,需要做一件事,运行时类型检查,vector的设计没有考虑这个,所以这件......
traits编程技法和模板元编程基础(2008-01-30 14:16:00)
摘要:哈,我发现我终于成为标题党啦,全拿一些powerful的词做标题,骗点击率。说实话,在学校写论文全是有目的的,保研,升学,拿学位等,其实写的都是些什么哦,这里抄那里抄,能把专家组唬到就是好文章,写博客不同,我爱写就写,你当我是炫耀也好,轻浮也罢,都不放在耳边。
C++语言其实一点都不强大,强大的是那些使用C++的牛人们!
1,traits技法
这个说法我是从《STL源码剖析》中看到的,但是真正明白还是后来慢慢想才明白的。traits的意思是‘特性’,《Eff C++》里说将类的设计视为类型的设计,把class的设计当成type的设计,C++提供了一种无限的可能,使你可以不断的设计新的类型,从而使语言本身更强大。面对各种class,语言本身也没有提供太多的东西去描述‘特性’,有些知识是一开始不可预料的,所以没办法定义,比如,可拷贝性。如果语言定义一开始就很注重这个概念,那可能会多一个关键字操作符,譬如copiable()。在C的时代有sizeof(),在C++的时候有typeof,开玩笑,是typeid,但是这是在运行时,但是有了traits技法,这个是不必要的,将这些事情给编译器在编译期运行,会给运行程序带来更多的效率。我们知道型别本身这个信息也可以看成是基本的‘特性’。
在编译期做‘特性’检查,就是做类似这样的事情:
template<typename T>
void doSomething(T& t)
{
if(t 具有某种特性)
{
做一些事情
}
else
{
做另一些事情
}
}
在运行期做,可以这样设计T
class T
{
public:
T() : copiable(true) {}
virtual ~T() ......
[笔记]什么是封装(2008-01-24 23:48:00)
摘要:初学COM,突然觉得,这真是个好东西,天啦,我终于开始学十几年前的微软技术了。。。
脑子里有太多东西,不知道从哪里记,就从封装开始吧。大学里学的是C(因为专业问题),但是我自学的是C++,我当时以为C++是C的扩充,就像C2。0一样。说C++是面向对象,C是面向过程,这一年级的学生都能脱口而出,但到底为什么要面向对象,为什么数据结构+算法的模型不能胜任现在的大型软件开发?面向对象把它变成了
对象=数据结构+算法
程序=对象1+对象2+。。。
为什么不能胜任?如果你做为开发者,然后去听一下那些完全不懂计算机的客户老板对项目的不断改变的需求,甚至到无耻的地步的时候,你就会彻底抛弃你对C编程的自信。
我没有说C不好,在很多方面,C比C++好,C++就好像迈了一步,但是还是有很多问题需要解决,一条脚还在原地。
对象就是对数据结构和算法的封装的实例。把数据和方法绑在一起,就叫一个类,类一实例化,就是对象。从代码上看,就像这样
class A
{
int data;
public:
void foo();
};
Thinking in C++一书里前面部分专门讲,这样做的来源,因为很早C程序员就是这样想的,只是形式上没有这样做,比如
struct A
{
int data;
};
void foo(struct A*);
这有什么区别?除了好看些,名字可以有更好的一些作用约束之外,似乎没有任何进步啊。同样,C++的编译器也正是这样做的,将上面的class A进行sizeof(A)运算,结果是4,那个成员函数被编译器移出了类,非常巧妙地欺骗了天真的C++初学者。同时,编译器在调用成员函数的时候,将this指针偷偷换进去,实质上这和C的代码一模一样,而一年级的学生可能会因为自己学了C++,能写出一些用类描述的程序,而觉得非常了不起。
你看不下去了,我这里没有干系到类的继承,你要说多态啊,没有多态哪里是面向对象?很多人这样说。可不可以先不想后面的事情,为什么要用类,为什么要用面向对象,都不明不白,就接着讨论,是不是有点像站在空中说话一样,一点不踏实。
我要说的封装,应该是,接口和实现的分开。接口是功能的描述,是交给用户的按钮,实现是设计人员的事情。非常不巧,其实面向......