在c语言中,大家都学习过结构体struct,那么我们来看以下c语言下的一个struct例子
1 /************************************************************************* 2 > File Name: CLib.h 3 > Author: 阿瞒123 4 > Mail: caopp123@126.com 5 > Created Time: 2016年05月26日 星期四 04时28分44秒 6 ************************************************************************/ 7 8 typedef struct CStashTag{ 9 int size; 10 int quantity; 11 int next; 12 unsigned char* storage; 13 }CStash; 14 15 void initialize(CStash *s,int size); 16 void cleanup(CStash *s); 17 int add(CStash *s,const void* element); 18 void *fetch(CStash *,int index); 19 int count(CStash *s); 20 void inflate(CStash *s,int increase);
上面的是一个结构体,和操作结构体上数据的一些函数的声明,我们特别注意的是这些函数的第一个参数,都是一个CStash *类型。
1 #include"CLib.h" 2 #include<iostream> 3 #include<cassert> 4 //void infalte(CStash *s, int increase); 5 using namespace std; 6 const int increment=100; 7 8 void initialize(CStash *s,int sz){ 9 s->size=sz; 10 s->quantity=0; 11 s->next=0; 12 s->storage=NULL; 13 } 14 15 int add(CStash *s,const void* element){ 16 17 if(s->next>=s->quantity) 18 inflate(s,increment); 19 int startBytes=s->next*s->size; 20 unsigned char* e=(unsigned char*)element; 21 for(int i=0;i<s->size;i++) 22 s->storage[startBytes+i]=e[i]; 23 s->next++; 24 return (s->next-1); 25 } 26 27 void *fetch(CStash *s,int index){ 28 assert(0<=index); 29 if(index>=s->next) 30 return 0; 31 return &(s->storage[index*s->size]); 32 } 33 34 int count(CStash *s){ 35 return s->next; 36 } 37 38 void inflate(CStash *s, int increase){ 39 assert(increase>0); 40 int newQuantity=s->quantity+increase; 41 int newBytes=newQuantity*s->size; 42 int oldBytes=s->quantity*s->size; 43 unsigned char* b=new unsigned char[newBytes]; 44 for(int i=0;i<oldBytes;i++) 45 b[i]=s->storage[i]; 46 delete [](s->storage); 47 s->storage = b; 48 s->quantity=newQuantity; 49 } 50 51 void cleanup(CStash *s){ 52 if(s->storage!=0){ 53 cout<<"freeing storage"<<endl; 54 delete []s->storage; 55 } 56 }
上面的代码是CLib.h声明函数的实现
1 /************************************************************************* 2 > File Name: CLib.h 3 > Author: 阿瞒123 4 > Mail: caopp123@126.com 5 > Created Time: 2016年05月26日 星期四 04时28分44秒 6 ************************************************************************/ 7 8 #include<iostream> 9 #include"CLib.h" 10 #include<fstream> 11 #include<string> 12 #include<cassert> 13 using namespace std; 14 15 int main(){ 16 CStash intStash;//定一个一个存储int类型的CStash结构体的变量 17 int i; 18 const int bufsize=80; 19 initialize(&intStash,sizeof(int)); 20 for(int i=0;i<100;i++) 21 add(&intStash,&i); 22 for(i=0;i<count(&intStash);i++) 23 cout<<"fetcha(&intStash,"<<i<<")="<<*(int*)fetch(&intStash,i)<<endl; 24 cleanup(&intStash); 25 26 CStash stringStash;//定一个一个存储string类型的CStash结构体的变量 27 char *cp; 28 int k = 0; 29 string line; 30 ifstream in; 31 in.open("CLibTest.cpp"); 32 initialize(&stringStash,sizeof(char)*bufsize); 33 while (getline(in, line)) 34 add(&stringStash,line.c_str()); 35 36 while ((cp=(char*)fetch(&stringStash,k++))!=0) 37 cout << "fetcha(&stringStash," << k << ")=" <<cp << endl; 38 39 cleanup(&stringStash); 40 system("pause"); 41 return 0; 42 }
上面的代码是一个创建了两个CStash类型的变量,一个用于存放int类型,一个用于存放string类型。
上面的一个例子我们只要注意,我们用CStash结构体定义了变量,如果要用函数作用与这些函数,需要给函数传递变量的地址,也就是每个函数的第一个参数。这是结构体,那么类呢?看下面的代码
1 /************************************************************************* 2 > File Name: CLib.h 3 > Author: 阿瞒123 4 > Mail: caopp123@126.com 5 > Created Time: 2016年05月26日 星期四 04时28分44秒 6 ************************************************************************/ 7 struct Stash{ 8 public: 9 int size;//Size of each space 10 int quantity;//number of storage spaces 11 int next;//next empty space 12 unsigned char* storage; 13 void initialize(int size); 14 int add(const void* element); 15 void cleanup(); 16 void* fetch(int index); 17 int count(); 18 void inflate(int increase); 19 };
我们把函数在结构体中声明了,而不是声明在结构体的外部,并且这些函数都没有了结构体类型的指针变量了。我们看下面的实现
1 /************************************************************************* 2 > File Name: CLib.h 3 > Author: 阿瞒123 4 > Mail: caopp123@126.com 5 > Created Time: 2016年05月26日 星期四 04时28分44秒 6 ************************************************************************/ 7 8 #include"CppLib.h" 9 #include<iostream> 10 #include<cassert> 11 using namespace std; 12 const int increment=100; 13 14 void Stash::initialize(int sz){ 15 size=sz; 16 quantity=0; 17 next=0; 18 storage=0; 19 } 20 21 int Stash::add(const void* element){ 22 if(next>=quantity) 23 inflate(increment); 24 int startBytes=next*size; 25 unsigned char* e=(unsigned char*)element; 26 for(int i=0;i<size;i++) 27 storage[startBytes+i]=e[i]; 28 next++; 29 return (next-1); 30 } 31 32 void* Stash::fetch(int index){ 33 assert(0<=index); 34 if(index>=next)// To indicate the end 35 return 0; 36 return &(storage[index*size]); 37 } 38 39 int Stash::count(){ 40 return next; 41 } 42 43 void Stash::inflate(int increase){ 44 assert(increase>0); 45 int newQuantity=quantity+increase; 46 int newBytes=newQuantity*size; 47 int oldBytes=quantity*size; 48 unsigned char* b=new unsigned char[newBytes]; 49 for(int i=0;i<oldBytes;i++) 50 b[i]=storage[i]; 51 delete [] storage; 52 storage=b; 53 quantity=newQuantity; 54 } 55 56 void Stash::cleanup(){ 57 if(storage!=0){ 58 cout<<"freeing storage"<<endl; 59 delete []storage; 60 } 61 }
在实现时,我们也没有显式的传递给函数结构体类型的指针。再看实例。
1 /************************************************************************* 2 > File Name: CLib.h 3 > Author: 阿瞒123 4 > Mail: caopp123@126.com 5 > Created Time: 2016年05月26日 星期四 04时28分44秒 6 ************************************************************************/ 7 8 #include"require.h" 9 #include"CppLib.h" 10 #include<fstream> 11 #include<string> 12 #include<iostream> 13 using namespace std; 14 15 int main(){ 16 Stash intStash; 17 intStash.initialize(sizeof(int)); 18 for(int i=0;i<20;i++) 19 intStash.add(&i); 20 for(int j=0;j<intStash.count();j++){ 21 cout << "intStash.fetch(" << j << ")=" << *(int*)intStash.fetch(j)<< endl; 22 } 23 return 0; 24 }
上面的所有代码可以得出一个结论,其实哪怕我们把操作结构体变量的函数声明在结构体的内部,还是要给这些函数传递一个结构体变量的指针,一个结构体可以定义多个变量,如果不把结构体变量的指针传递给这些操作结构体变量,这些函数怎么知道它是操作了哪一个变量呢?在c语言中,这个结构体变量指针是我们显式的传递给函数的,那么进入面向对象的c++语言,我们把操作结构体遍的函数和结构体内的数据都声明在一个结构体中(其实就是类),这叫封装。此时虽然我们没有显式的把结构体变量传递给函数,但是编译器帮我做了这件事,编译器会隐式的传递给函数一个结构体变量的指针,这个指针变量就是this。
既然类是结构体演变而来,我们知道在c语言中,无论结构体定义多少个变量,而操作这些变量的函数都是一样的,只不过把变量的地址传递个函数,让函数知道自己操作的是哪一个变量,类同样如此,无论我们用类定义了多少个变量(也成为对象),这些遍都共享一样的函数。编译器通过传递给变量this指针来确定操作的变量是哪一个。
既然传递给函数的是this指针,那么this指针的类型是什么样的呢?《c++大学教程(c++ How to program)》第七版中的347页给出了下面的一段话:
每一个对象都可以使用一个成为this的指针来访问自己的地址,对象的this指针不是对象本身的一部分,也就是this指针占用的内存大小不会反映在对对象进行sizeof运算得到结果中。相反this指针作为一个隐式的参数被传递给对象的每一个非static成员函数。操作对象的函数通过this指针来区分用户定义的多个对象。
this指针的类型:
this指针的类型取决于对象的类型及使用this的成员函数是否被声明为const。
例如我们有一个类Employee,在Employee类中有一些非const成员函数,那么传递给这些非const成员函数的this指针类型就是Employee * const, 注意const在*号和指针变量名之间,那就代表const只是修饰了指针变量,代表这个指针变量的值不能改变,即指针指向的地址不能改变,但是指针指向地址里面存储的值是可以变的。
当然类Employee中还有一些const的成员函数,那么此时传递给这些成员函数的this指针的类型是const Employee* const 代表this的值不能改变,this指针指向地址的变量的值也不能改变。
通过上面的标为红色的字,我们来解释一下为什么const对象不能调用非const函数,这就是因为const对象的指针类型是 const 类名* const ,而非const成员函数的this指针类型是 类名* const,这就会出现实参和形参的类型不匹配,所以就会报错,一般的报错信息就是“const 类名* const cann't convert to 类名* const”
但是我们知道一个非const对象是可以调用const函数的,是因为最小权限原则。