转载一好文章,JAVA和C++
1. 运算符重载(STL方法库的的基本骨架)
C++的粉丝视为美酒而Java粉丝视为毒药的东西。观点不同而已。我为什么强调这一点是C++的精髓呢?
举个例子,我们设计一个算法,熟悉软件工程的朋友可能立即会提出: 首先设计一个虚基类,基类里面实现算法的框架,然后实现一个继承类,来实现具体的一些算法细节,不同的子类有不同的实现,这样算法就可以有几个大的版本。 但是问题是,如果算法处理的对象类型不定呢? 那么就把算法用模板来实现。----问题来了,算法处理不同类型的值的时候,需要做不同的处理流程,那么是否每一个继承类的Overriden函数都得为 不同的类型设计不同的处理流程呢?(例如数字类型,字符串类型,大的二进制类型都需要不同的处理流程)
C++在这里给出了一个天才的,通用的处理流程: 把数据类型包装成为能够自描述的,有基本类型语义的"值"!
听起来有点拗口(可能我的概括有点啰嗦),但是是这样的: 例如我写一个快速排序算法qsort<T>(用模板),写完了以后可以排序所有的基本类型。那么当我需要排序复杂类型(class B)的时候怎么办呢?
我只需要:(1)重载B类型的比较运算符'<','>','=','=='
bool operator<(const B& b)
bool operator>(const B& b)
bool operator==(const B& b)
B& operator=(const B&b)
(2)重载B类型的iostream运算符,例如我需要在排序的过程输出一些信息到控制台或其他地方:
friend ostream& operator<<(ostream& os,const B&b) (当然要在算法实现里面使用iostream而不是stdio)
这样的话,类型B就成为了一个具有"值"语义的类,可以直接作用于qsort<T>而无需任何修改!
用虚基类/继承类的话,需要在继承类的覆盖方法里面去实现一个算法细节的小部分,可能导致需要大量的调试,或者不同类型的处理流程之间不具有通用性,算法 本省也会变得难以理解。而使用运算符重载,我们在增加一个类型的时候,只需要为这个类型的内部作一些增量的重载函数,算法实体做到了100%通用性。软件 工程追求的目的就是鼓励增删而避免修改,不是吗?
当然,在算法框架设计的方面,"谓词对象"和"算子"的思想在C++的STL库和Java的swing监听对象里面都有体现。
2. 强类型的模板和元类型推导。当然Java1.5版本以后也引入了范型和模板的机制,但是它只用于编译时的正确性检查,运行时的信息仍然是object类动 态决定的(只有一套代码)。而C++的模板->代码生成,从头到尾都是编译时的强类型检查(N个实例化类型就产生N套代码,彼此互不影响),因此有 了模板的推导功能,以至于可以进行"元编程"的工作。
有兴趣的朋友可以参考"C++ template"这本书的内容,非常深入的讲解C++模板的制----它和Java的模板机制在实现上有很大的不同,虽然两者都支持范型的概念。
3. 标准库行为重载
想知道哪里分配的内存没有被释放吗? 我可不想修改已有的程序检查问题,在检查完毕后再删除这些增加的代码,我只想做一个一劳永逸的增量检查,怎么办?
C++给你提供了!(Java没有的!) 你可以重载标准运算符new/delete,你可以重定义异常处理机制中的默认处理过程!
3.1 new/delete的重载:
(a)
你可以在不同的作用域层次重载new/delete运算符,例如在一个类定义当中去重载
class B{
public:
void* operator new(size_t s...){
cout<<"声明一个类B的指针并分配内存\n";
return ::new(B);
}
void operator delete(size_t s){
cout<<"释放类B的一个指针内存\n";
::delete s;
}};
能重载delete[] 吗? 重载new[]吗? 可以
(b)使用replacement new
在一个固定的管局内存块上分配和释放
static B place[3];
B* pb=new(place)B;//单个
B* pbv=new(place)B[2];//数组
析构的时候不是delete而是显示调用pb->~B();
可以replacement一个兑现数组:
X* p6=new(a)X[10];
(c)覆盖一般作用域的new/delete,例如某个cpp文件作用域
void* operator new(size_t t){
cout<<"重载的new\n";
return (void*)::malloc(t);
}
void operator delete(void* p){
cout<<"重载的delete\n";
::free(p);
}
之后在这个cpp文件里面,new和delete就是调用的这个重载的版本
3.2 异常处理的重载
我们都知道,try块内部的对象,在try抛出异常时,都能被catch模块之前调用析构函数,发生错误的情况是: 如果在catch的处理流程中,有对象的析构函数发生,仍然是抛出unexpected_exception异常,那么这个异常经常会导致 std::terminate()发生,程序可能就退出了。怎么捕捉呢? 异常处理的函数可以用std::set_unexpected()和std::set_terminate()来保存和设置。也就是说,C++的所有标 准异常流程都能够被重载。
#include<cstdio>
#include<exception>
#include<iostream>
#include<fstream>
using namespace std;
typedef void(*unexpected_handle)();
void f(){ printf("I get unexpected exception\n"); }
void t(){ printf("I terminate it\n"); }
struct C{
C(){ printf("ctor\n"); }
void f(){ throw std::exception(); }
~C(){ printf("dtor\n"); }
};
int main(void){
std::set_unexpected(f);
std::set_terminate(t);
try{
C c1;
c1.f();
}catch(...){
bool ue=std::uncaught_exception();
printf("catch,b=%d\n",ue);
C c1;
c1.f();
}
return 0;
}
当一个未知的异常发生时,自定义的函数t()就被调用了。
vector构造函数(10)->分配10个元素,push_back的话就放在第11个元素的位置
#include<vector>
#include<cstdio>
using namespace std;
int main(void){
vector<int> v(3);
v.push_back(2);
printf("%d\n",v.size());
for(vector<int>::iterator it=v.begin();it!=v.end();++it){
printf("%d,",*it);
}
return 0;
}