C++面向对象模型初探
C++对象模型可以概括为以下2部分:
- 语言中直接支持面向对象程序设计的部分,主要涉及如构造函数、析构函数、虚函数、继承(单继承、多继承、虚继承)、多态等等。
- 对于各种支持的底层实现机制。
C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类。
从计算机的角度,程序依然由数据段和代码段构成。
C++编译器如何完成面向对象理论到计算机程序的转化?
换句话:C++编译器是如何管理类、对象、类和对象之间的关系
class MyClass01{
public:
int mA;
};
class MyClass02{
public:
int mA;
static int sB;
};
class MyClass03{
public:
void printMyClass(){
cout << "hello world!" << endl;
}
public:
int mA;
static int sB;
};
class MyClass04{
public:
void printMyClass(){
cout << "hello world!" << endl;
}
static void ShowMyClass(){
cout << "hello world!" << endl;
}
public:
int mA;
static int sB;
};
int main(){
MyClass01 mclass01;
MyClass02 mclass02;
MyClass03 mclass03;
MyClass04 mclass04;
cout << "MyClass01:" << sizeof(mclass01) << endl; //4
//静态数据成员并不保存在类对象中
cout << "MyClass02:" << sizeof(mclass02) << endl; //4
//非静态成员函数不保存在类对象中
cout << "MyClass03:" << sizeof(mclass03) << endl; //4
//静态成员函数也不保存在类对象中
cout << "MyClass04:" << sizeof(mclass04) << endl; //4
return EXIT_SUCCESS;
}
通过上面的案例,我们可以的得出:C++类对象中的变量和函数是分开存储。
编译器对属性和方法的处理机制
C++类对象中的成员变量和成员函数是分开存储的
成员变量:
- 普通成员变量: 存储于对象中,与struct变量有相同的内存布局和字节对齐方式
- 静态成员变量: 存储于全局数据区中
成员函数: 存储于代码段中。
问题出来了:很多对象共用一块代码?代码是如何区分具体对象的那?
换句话说:int getK() const { return k; },代码是如何区分,具体obj1、obj2、obj3对象的k值?
那就要说到C++编译器的另一个处理方法了
C++编译器对普通成员函数的内部处理
编译器处理成员函数时会自己带一个this指针,用于指向当前类的数据。
总结
- C++类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效!
- C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。
- 静态成员函数与普通成员函数的区别:
静态成员函数不包含指向具体对象的指针
普通成员函数包含一个指向具体对象的指针
this指针
this指针工作原理
通过上例我们知道,c++的数据和操作也是分开存储,并且每一个非内联成员函数(non-inline member function)只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
c++通过提供特殊的对象指针,this指针,解决上述问题。This指针指向被调用的成员函数所属的对象。
c++规定,this指针是隐含在对象成员函数内的一种指针。当一个对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针this,用以保存这个对象的地址,也就是说虽然我们没有写上this指针,编译器在编译的时候也是会加上的。
因此this也称为“指向本对象的指针”,this指针并不是对象的一部分,不会影响sizeof(对象)的结果。
this指针永远指向当前对象。
注意: 静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量。
this指针的使用
- 当形参和成员变量同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this。
class Person{
public:
//1. 当形参名和成员变量名一样时,this指针可用来区分
Person(string name,int age){
//name = name;
//age = age; //输出错误
this->name = name;
this->age = age;
}
//2. 返回对象本身的引用
//重载赋值操作符
//其实也是两个参数,其中隐藏了一个this指针
Person PersonPlusPerson(Person& person){
string newname = this->name + person.name;
int newage = this->age + person.age;
Person newperson(newname, newage);
return newperson;
}
void ShowPerson(){
cout << "Name:" << name << " Age:" << age << endl;
}
public:
string name;
int age;
};
//3. 成员函数和全局函数(Perosn对象相加)
Person PersonPlusPerson(Person& p1,Person& p2){
string newname = p1.name + p2.name;
int newage = p1.age + p2.age;
Person newperson(newname,newage);
return newperson;
}
int main(){
Person person("John",100);
person.ShowPerson();
cout << "---------" << endl;
Person person1("John",20);
Person person2("001", 10);
//1.全局函数实现两个对象相加
Person person3 = PersonPlusPerson(person1, person2);
person1.ShowPerson();
person2.ShowPerson();
person3.ShowPerson();
//2. 成员函数实现两个对象相加
Person person4 = person1.PersonPlusPerson(person2);
person4.ShowPerson();
system("pause");
return EXIT_SUCCESS;
}
全局函数PK成员函数
- 把全局函数转化成成员函数,通过this指针隐藏左操作数
Test add(Test &t1, Test &t2)===》Test add(Test &t2)
- 把成员函数转换成全局函数,多了一个参数
void printAB()===》void printAB(Test *pthis)
- 函数返回元素和返回引用
Test& add(Test &t2) //*this //函数返回引用
{
this->a = this->a + t2.getA();
this->b = this->b + t2.getB();
return *this; //*操作让this指针回到元素状态
}
Test add2(Test &t2) //*this //函数返回元素
{
//t3是局部变量
Test t3(this->a+t2.getA(), this->b + t2.getB()) ;
return t3;
}
const修饰成员函数
- 用const修饰的成员函数时,const修饰this指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量,
- 当成员变量类型符前用mutable修饰时例外。
//const修饰成员函数
class Person{
public:
Person(){
this->mAge = 0;
this->mID = 0;
}
//在函数括号后面加上const,修饰成员变量不可修改,除了mutable变量
void sonmeOperate() const{
//this->mAge = 200; //mAge不可修改
this->mID = 10; //const Person* const tihs;
}
void ShowPerson(){
cout << "ID:" << mID << " mAge:" << mAge << endl;
}
private:
int mAge;
mutable int mID;
};
int main(){
Person person;
person.sonmeOperate();
person.ShowPerson();
system("pause");
return EXIT_SUCCESS;
}
const修饰对象(常对象)
- 常对象只能调用const的成员函数
- 常对象可访问 const 或非 const 数据成员,不能修改,除非成员用mutable修饰
class Person{
public:
Person(){
this->mAge = 0;
this->mID = 0;
}
void ChangePerson() const{
mAge = 100;
mID = 100;
}
void ShowPerson(){
cout << "ID:" << this->mID << " Age:" << this->mAge << endl;
}
public:
int mAge;
mutable int mID;
};
void test(){
const Person person;
//1. 可访问数据成员
cout << "Age:" << person.mAge << endl;
//person.mAge = 300; //不可修改
person.mID = 1001; //但是可以修改mutable修饰的成员变量
//2. 只能访问const修饰的函数
//person.ShowPerson();
person.ChangePerson();
}