该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
个人以为C++类有一个很大的缺陷,就是类的声明必须要暴露内部的数据结构,这实际上是和模块化理念违背的,因为这样一来,类的内部数据结构就和类之外的环境耦合在一起了,如果类的数据变了,哪怕只改个名字,所有与之相关的代码模块都受影响,最起码要重编译,更不用说造成了其它的问题;而真正模块化的理念就是,函数与数据结构是分离开的,模块对外影响的只是函数接口,而数据结构被封装在模块内部是不可见的,其变化只影响模块本身,不会影响到模块之外的环境;
C++的类之所以会有这个问题,可能是与C语言的结构体相关,因为从C++的历史来看,类就是结构体的改进和衍生;C语言中的结构体是不在乎模块化的,因为它只是数据的聚合物而已,模块化在C语言中通过其它机制实现的,结构体本身并不负责模块化,所以它也不在乎内部数据的暴露什么的;所以,衍生自C语言结构体的类就受到限制了;
当然,C++的类要想真正实现模块化,将函数接口和数据分离开,也是可以做到的,但那只能用间接的方式了,还必须要做很多琐碎的事情,比如,在虚函数和继承的机制基础上,用上一大堆的设计模式来实现,这使得原本应该直接面对和解决的问题一下子变的间接和复杂起来,更不用说其中代码编写和维护的负担,运行时性能的负担;而且,从语言上来说,一旦采取这种间接的模块化机制,类的对象就无法放置在栈上了,只能在全局变量或者堆上实现对像的实例化,这很大程度上限制了代码的适应性和灵活性,打个比方,在全局变量和堆空间受限的情况下,这种模块化就很难实现;而且最麻烦的问题在于,C++是没有垃圾回收机制的,如果将对像实例化在堆上,那对这个对像指针的维护就是一种负担;而如果将对像实例化为全局变量,在空间不受限的情况下这或许问题不大,但这样一来其方式等于又回到了C语言的模块化方式,那我为什么不直接用C语言?
所以,个人以为,C++的类语法需要改进,在原先的类的基础上,增加“模块类”的概念,增加两个关键字abstract和instance,接在class后边,class abstract是类的接口声明,放在头文件中,class instance是实现类的功能和数据结构,放在.cpp文件中,这样,就将类分离成了函数接口(abstract)和数据实现(instance)两部分,两者结合就实现了一个“模块类”,在语言的语法上就支持了类的模块化;在此基础上,C++的编译器还必须要有一个改进,那就是有关类instance的信息要做预处理以及链接时的处理,从编译预处理阶段和链接阶段获得的类的大小这个信息,在不暴露类的内部数据结构的情况下,可以让编译器在栈上分配空间;
而且这还有一个更大的好处,因为class被分解成了abstract和instance两个部分,那instance部分就可以封装在库里了,比如lib文件,dll文件,这样就可以直接实现C++类的二进制封装和复用了,而不必要用COM这样间接的复杂晦涩的东西,而且COM是不支持继承的,而C++改进后的“模块类”可以直接实现继承的,因为类的一些信息(比如sizeof()算大小等等)在库文件里可以导出,这样编译器在预处理阶段和链接阶段就可以得到父类和子类的空间布局,从而在代码和二进制层面上都实现了类的继承;
举个例子吧:
//CTest.h
class abstract CTest
{
public:
CTest();
~CTest();
void fun1();
void fun2();
void fun3();
public:
extern int data1;
private:
extern int data4;
};
//CTest.cpp
class instance CTest
{
public:
CTest()
{
data1 = data2 = data3 = data4 = 1;
}
~CTest()
{
data1 = data2 = data3 = data4 = 0;
}
void fun1()
{
cout<
}
void fun2()
{
cout<
}
void fun3()
{
cout<
}
void fun4()
{
cout<
}
public:
int data1;
public:
int data2;
private:
int data3;
private:
int data4;
};
//main.cpp
int main()
{
CTest test; //test可以在栈上实例化,因为编译器在预处理阶段获得了class instance CTest的信息,可以算出CTest大小,从而在栈上分配空间实例化;
test.fun1();
test.fun2();
test.fun3();
test.fun4(); //错误,因为CTest::fun4()虽然是public的,但是没有在class abstract CTest声明,所以在main.cpp中是不可见的;
cout<
cout<
cout<
cout<
return 0;
}
在这样一个例子中,如果把CTest.cpp换成CTest.lib的二进制库,程序照样正常工作, 编译器在预处理阶段可以从CTest.lib中导出CTest的大小,以及导出CTest的构造函数CTest()和析构函数~CTest()以及其它信息,这样就可以在栈上给test分配空间实例化了;