面向对象模型初探
1.1 成员变量和函数的存储

-
在c语言中,“分开来声明的,也就是说,语言本身并没有支持“数据”和“函数”之间的关联性,我们把这种程序方法称为“程序性的”,由一组“分布在各个以功能为导航的函数中”的算法驱动,它们处理的是共同的外部数据。 -
c++实现了“封装”,那么数据(成员属性)和操作(成员函数)是什么样的呢? “数据”和“处理数据的操作(函数)”是分开存储的。 c++中的非静态数据成员直接内含在类对象中,就像c struct一样。 成员函数(member function)虽然内含在class声明之内,却不出现在对象中。 每一个非内联成员函数(non-inline member function)只会诞生一份函数实例. 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;
cout << "MyClass02:" << sizeof(mclass02) << endl;
cout << "MyClass03:" << sizeof(mclass03) << endl;
cout << "MyClass04:" << sizeof(mclass04) << endl;
return EXIT_SUCCESS;
}
1.2 this指针
1.2.1 this指针工作原理
通过上例我们知道,c++的数据和操作也是分开存储,并且每一个非内联成员函数(non-inline member function)只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码 那么问题是:这一块代码是如何区分那个对象调用自己的呢?
- c++通过提供特殊的对象指针,this指针,解决上述问题。This指针指向被调用的成员函数所属的对象。
- c++规定,this指针是隐含在对象成员函数内的一种指针。当一个对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针this,用以保存这个对象的地址,也就是说虽然我们没有写上this指针,编译器在编译的时候也是会加上的。因此this也称为“指向本对象的指针”,this指针并不是对象的一部分,不会影响sizeof(对象)的结果。
- this指针是C++实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每一个对象都拥有自己的函数成员。一般情况下,并不写this,而是让系统进行默认设置。
this指针永远指向当前对象。
成员函数通过this指针即可知道操作的是那个对象的数据。This指针是一种隐含指针,它隐含于每个类的非静态成员函数中。This指针无需定义,直接使用即可。 注意:静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量。
c++编译器对普通成员函数的内部处理

1.2.2 this指针的使用
当形参和成员变量同名时,可用this指针来区分 在类的非静态成员函数中返回对象本身,可使用return *this. this的链式编程(*this指向指针对象本身)  空指针访问成员函数是否保错,取决于成员函数中是否用到this指针
class Person{
public:
Person(string name,int age){
this->name = name;
this->age = age;
}
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;
};
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);
Person person3 = PersonPlusPerson(person1, person2);
person1.ShowPerson();
person2.ShowPerson();
person3.ShowPerson();
Person person4 = person1.PersonPlusPerson(person2);
person4.ShowPerson();
system("pause");
return EXIT_SUCCESS;
}
1.2.3 const修饰成员函数(常函数)
用const修饰的成员函数时,const修饰this指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量, 当成员变量类型符前用mutable修饰时例外。
class Person{
public:
Person(){
this->mAge = 0;
this->mID = 0;
}
void sonmeOperate() const{
this->mID = 10;
}
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;
}
1.2.4 const修饰对象(常对象)
常对象只能调用const的成员函数 常对象可访问数据成员,不能修改,除非成员用mutable修饰
class Person{
public:
Person(){
this->mAge = 0;
this->mID = 0;
}
void ChangePerson() const{
mAge = 100;
mID = 100;
}
void ShowPerson(){
this->mAge = 1000;
cout << "ID:" << this->mID << " Age:" << this->mAge << endl;
}
public:
int mAge;
mutable int mID;
};
void test(){
const Person person;
cout << "Age:" << person.mAge << endl;
person.mID = 1001;
person.ChangePerson();
}
1.3 友元
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么办? 解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员。这一点从现实生活中也可以很好的理解: 比如你的家,有客厅,有你的卧室,那么你的客厅是Public的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去。 程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。
1.3.1 友元语法
friend关键字只出现在声明处 其他类、类成员函数、全局函数都可声明为友元 友元函数不是类的成员,不带this指针 友元函数可访问对象任意成员属性,包括私有属性
class Building;
class MyFriend{
public:
void LookAtBedRoom(Building& building);
void PlayInBedRoom(Building& building);
};
class Building{
friend void CleanBedRoom(Building& building);
#if 0
friend void MyFriend::LookAtBedRoom(Building& building);
friend void MyFriend::PlayInBedRoom(Building& building);
#else
friend class MyFriend;
#endif
public:
Building();
public:
string mSittingRoom;
private:
string mBedroom;
};
void MyFriend::LookAtBedRoom(Building& building){
cout << "我的朋友参观" << building.mBedroom << endl;
}
void MyFriend::PlayInBedRoom(Building& building){
cout << "我的朋友玩耍在" << building.mBedroom << endl;
}
void CleanBedRoom(Building& building){
cout << "友元全局函数访问" << building.mBedroom << endl;
}
Building::Building(){
this->mSittingRoom = "客厅";
this->mBedroom = "卧室";
}
int main(){
Building building;
MyFriend myfriend;
CleanBedRoom(building);
myfriend.LookAtBedRoom(building);
myfriend.PlayInBedRoom(building);
system("pause");
return EXIT_SUCCESS;
}
[友元类注意]
1.友元关系不能被继承。 2.友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。 3.友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友。
思考: c++是纯面向对象的吗? 如果一个类被声明为friend,意味着它不是这个类的成员函数,却可以修改这个类的私有成员,而且必须列在类的定义中,因此他是一个特权函数。c++不是完全的面向对象语言,而只是一个混合产品。增加friend关键字只是用来解决一些实际问题,这也说明这种语言是不纯的。毕竟c++设计的目的是为了实用性,而不是追求理想的抽象。 — Thinking in C++
1.3.2 课堂练习
请编写电视机类,电视机有开机和关机状态,有音量,有频道,提供音量操作的方法,频道操作的方法。由于电视机只能逐一调整频道,不能指定频道,增加遥控类,遥控类除了拥有电视机已有的功能,再增加根据输入调台功能。
提示:遥控器可作为电视机类的友元类。
class Remote;
class Television{
friend class Remote;
public:
enum{ On,Off };
enum{ minVol,maxVol = 100 };
enum{ minChannel = 1,maxChannel = 255 };
Television(){
mState = Off;
mVolume = minVol;
mChannel = minChannel;
}
void OnOrOff(){
this->mState = (this->mState == On ? Off : On);
}
void VolumeUp(){
if (this->mVolume >= maxVol){
return;
}
this->mVolume++;
}
void VolumeDown(){
if (this->mVolume <= minVol){
return;
}
this->mVolume--;
}
void ChannelUp(){
if (this->mChannel >= maxChannel){
return;
}
this->mChannel++;
}
void ChannelDown(){
if (this->mChannel <= minChannel){
return;
}
this->mChannel--;
}
void ShowTeleState(){
cout << "开机状态:" << (mState == On ? "已开机" : "已关机") << endl;
if (mState == On){
cout << "当前音量:" << mVolume << endl;
cout << "当前频道:" << mChannel << endl;
}
cout << "-------------" << endl;
}
private:
int mState;
int mVolume;
int mChannel;
};
class Remote{
public:
Remote(Television* television){
pTelevision = television;
}
public:
void OnOrOff(){
pTelevision->OnOrOff();
}
void VolumeUp(){
pTelevision->VolumeUp();
}
void VolumeDown(){
pTelevision->VolumeDown();
}
void ChannelUp(){
pTelevision->ChannelUp();
}
void ChannelDown(){
pTelevision->ChannelDown();
}
void SetChannel(int channel){
if (channel < Television::minChannel || channel > Television::maxChannel){
return;
}
pTelevision->mChannel = channel;
}
void ShowTeleState(){
pTelevision->ShowTeleState();
}
private:
Television* pTelevision;
};
void test01(){
Television television;
television.ShowTeleState();
television.OnOrOff();
television.VolumeUp();
television.VolumeUp();
television.VolumeUp();
television.VolumeUp();
television.ChannelUp();
television.ChannelUp();
television.ShowTeleState();
}
void test02(){
Television television;
Remote remote(&television);
remote.OnOrOff();
remote.ChannelUp();
remote.ChannelUp();
remote.ChannelUp();
remote.VolumeUp();
remote.VolumeUp();
remote.VolumeUp();
remote.VolumeUp();
remote.ShowTeleState();
}
1.4 强化训练(数组类封装)
MyArray.h
#ifndef MYARRAY_H
#define MYARRAY_H
class MyArray{
public:
MyArray();
explicit MyArray(int capacity);
MyArray(const MyArray &array);
~MyArray();
void SetData(int pos, int val);
int GetData(int pos);
void PushBack(int val);
int GetLength();
int& operator[](int index);
private:
int mCapacity;
int mSize;
int* pAdress;
};
#endif
MyArray.cpp
#include"MyArray.h"
MyArray::MyArray(){
this->mCapacity = 100;
this->mSize = 0;
this->pAdress = new int[this->mCapacity];
}
MyArray::MyArray(int capacity){
this->mCapacity = capacity;
this->mSize = 0;
this->pAdress = new int[capacity];
}
MyArray::MyArray(const MyArray &array){
this->pAdress = new int[array.mCapacity];
this->mCapacity = array.mCapacity ;
this->mSize = array.mSize;
for(int i=0;i<array.mSize;i++){
this->pAdress[i] = array.pAdress[i];
}
}
MyArray::~MyArray(){
if (this->pAdress!=NULL){
delete[] this->pAdress;
this->pAdress=NULL;
}
}
void MyArray::SetData(int pos, int val){
if (pos < 0 || pos > mCapacity - 1){
return;
}
pAdress[pos] = val;
}
int MyArray::GetData(int pos){
return pAdress[pos];
}
void MyArray::PushBack(int val){
if (mSize >= mCapacity){
return;
}
this->pAdress[mSize] = val;
this->mSize++;
}
int MyArray::GetLength(){
return this->mSize;
}
int& MyArray::operator[](int index){
return this->pAdress[index];
}
TestMyArray.cpp
#include"MyArray.h"
void test(){
MyArray* array = MyArray myarray(50);
MyArray* arr1 = new MyArray(*array);
MyArray arr2 = *array;
delete array;
for (int i = 0; i < 50; i++){
myarray.PushBack(i);
}
for (int i = 0; i < myarray.GetLength(); i++){
cout << myarray.GetData(i) << " ";
}
cout << endl;
}
|