Day 11 C++面向对象编程 之 对象模型+this指针+友元
Day 11 C++面向对象编程 之 对象模型+this指针+友元
一、C++对象模型和this指针
1. 成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
C++编译器会给每个空对象分配一个字节的空间,是为了区分空对象占内存的位置,区分每一个空对象,每一个空对象也应该有一个独一无二的内存地址,进行区分。
若对象中有一个成员变量int,则一个对象占4个字节。只有非静态成员变量属于类的对象上,其他(静态成员变量、非静态成员函数、静态成员函数)都是独一份的,不属于类的对象上。
二、this指针的概念
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用一块代码。那么,这一块代码是如何区分是哪个对象调用的自己的呢?
c++通过提供特殊的对象指针,this指针,解决上述问题,this指针指向被调用的成员函数所属的对象(就是这个对象调用的它)。当对象p1调用这个函数,this就指向p1;对象p2调用这个函数,this就指向p2…
this指针是隐含在每一个非静态成员函数内的一种指针;this指针不需要定义,直接使用即可。
1. this指针的用途:
- 当形参和成员变量同名时,可用this指针来区分;this指针指向的被调用成员函数所属的对象(调用这个函数的对象)
- 在类的非静态成员函数中返回对象本身,可使用return *this
class Person
{
public:
Person(int age)
{
//age = age;//命名相同,系统会认为三个是同一个东西,错误
this->age = age;//命名相同,系统会认为三个是同一个东西,错误
}
void PersonAddAge(Person &p)
{
this->age += p.age;
}
int age;
};
//1.解决名称冲突
void test01()
{
Person p1(7);
cout << "p1的年龄:" << p1.age << endl;
}
//2.返回对象本身用*this
void test02()
{
Person p1(6);
Person p2(8);
p2.PersonAddAge(p1);
cout << "p2的年龄:" << p2.age << endl;
}
若这个函数本身返回的是一个void,所以不可以再调用成员函数,如果范围的是对象本身就可以,所以更改函数返回值类型
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);//错误
class Person
{
public:
...
Person& PersonAddAge(Person &p)//最前面加了引用&,表示一直对其进行修改,若不加引用表示采用了拷贝构造函数
{
this->age += p.age;
return *this;//*this指向的就是本体,返回的就是本体
}
}
void test()
{
Person p1(6);
Person p2(8);
//链式编程思想
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);//正确
}
若不加引用&,表示是返回值的方式,会创建新的对象,即第一次调用结束后返回的就是一个拷贝后的p2’,而不是p2,第二次返回的是p2’',…
若加引用&,表示用引用的方式返回,不会创建新的对象,会一直返回原本的对象。
this指针的本质是指针常量,指针常量,他是一个“常量”,指向不可以改,指向的值可以修改
三、空指针访问成员函数
c++中空指针也可以调用成员函数,但是也要注意有没有用到this指针,如果用到了this指针,需要加以判断保证代码的健壮性:
class Person
{
public:
void showClassName()
{
cout << "this is a Person Class" << endl;
}
void showPersonAge()
{
if (this == NULL)
{
return;//判断一下
}
cout << "age=" << m_age << endl;
//默认写成的是:cout << "age=" << this->m_age << endl;属性前面都会默认加this,表示是这个对象的属性
//错误,但是我们并没有创建对象,只是创建了一个这个类型的指针,所以传入指针是NULL,没有对象就就更别说其中的成员,无中生有,所以有错
//所以在前面要判断一下,增强代码的健壮性
}
int m_age;
};
void test01()
{
Person *p=NULL;//创建一个指针,指向空
p->showClassName();
p->showPersonAge();
}
错误:但是我们并没有创建对象,只是创建了一个这个类型的指针,所以传入指针是NULL,没有对象就就更别说其中的成员,无中生有,所以有错,所以在前面要判断一下,增强代码的健壮性
四、const修饰成员函数
1. 常函数:
- 成员函数后加const后,称这个函数为常函数;
- 常函数内不可以修改成员属性(值);
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
class Person
{
public:
//this指针的本质是指针常量,指针常量,他是一个“常量”,指向不可以改,指向的值可以修改
//Person *const this;这里可以修改值,但是不可以修改方向
//const Person *const this;值也不可以修改了,所以C++给加在了函数头的后面,修饰的是this指针,让指针指向的值也不可以修改
void showPerson() const
{
//m_a = 100;
//this->m_a = 100;
m_b = 200;
}
int m_a;
mutable int m_b;//进行这种声明之后,就可以更改值了
};
函数后+const,实际修饰的是this指针,让指针指向的值也不可以修改
2. 常对象:
- 声明对象前加const,称该对象为常对象;
- 常对象只能调用常函数
void test01()
{
const Person p;//常对象
//p.m_a = 300;//错误,有const不可修改
p.m_b=100;//正确,有mutable,可以修改
//常对象只能调用常函数
//p.func();//错误,不可调用普通成员函数,普通成员函数可以修改成员属性
p.showPerson();//正确,常对象只能调用常函数
}
常对象不可调用普通成员函数,因为普通成员函数可以修改成员属性
五、友元
生活中,一个房子有客厅(public),有卧室(private)。客厅都可以访问,卧室只有自己可以访问,以及允许的对象可以访问。在程序中,有些私有属性,也想让类外特殊的一些函数或者类进行访问,友元就是解决此类问题的。
友元的目的技术让一个函数或者类访问另一个类中的私有成员。关键字为 friend。有三种实现方式:
- 全局函数做友元、
- 类做友元、
- 成员函数做友元。
1. 全局函数做友元
class Building
{
friend void goodGay(Building* building);//+了friend,表示此全局函数是好朋友,可以访问building中的私有成员
public:
Building()
{
m_SittingRoom = "客厅";
m_BathRoom = "卧室";
}
string m_SittingRoom;//客厅
private:
string m_BathRoom;//卧室
};
//全局函数
void goodGay(Building *building)
{
cout << "好朋友的全局函数正在访问:" << building->m_SittingRoom << endl;
cout << "好朋友的全局函数正在访问:" << building->m_BathRoom << endl;
}
void test01()
{
Building building;
goodGay(&building);
}
要在类中最前面进行声明,告诉编译器,这个函数是好朋友,可以访问这个类的对象中的私有成员
2. 类做友元
class Building
{
friend class GoodGay;//GoodGay类是本类的好朋友,可以访问本类的私有成员
public:
Building();
public:
string m_SittingRoom;//客厅
private:
string m_BathRoom;//卧室
};
//类外写成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BathRoom = "卧室";
}
class GoodGay
{
public:
GoodGay();
void visit();//参观函数,访问building中的属性
Building* building;
};
GoodGay::GoodGay()
{
building = new Building;//创建对象维护成员变量
}
void GoodGay::visit()
{
cout << "好朋友的全局函数正在访问:" << building->m_SittingRoom << endl;
cout << "好朋友的全局函数正在访问:" << building->m_BathRoom << endl;
}
void test01()
{
GoodGay gay;
gay.visit();
}
3. 成员函数做友元
class Building;
class GoodGay
{
public:
GoodGay();
void visit();//参观函数,访问building中的属性
void visit2();//不可以访问
Building* building;
};
class Building
{
friend void GoodGay::visit();//GoodGay类的这个成员函数是本类的好朋友,可以访问本类的私有成员
public:
Building();//构造函数的声明
public:
string m_SittingRoom;//客厅
private:
string m_BathRoom;//卧室
};
//类外写成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BathRoom = "卧室";
}
GoodGay::GoodGay()
{
building = new Building;
}
void GoodGay::visit()
{
cout << "好朋友的visit函数正在访问:" << building->m_SittingRoom << endl;
cout << "好朋友的visit函数正在访问:" << building->m_BathRoom << endl;
}
void GoodGay::visit2()
{
cout << "好朋友2的visit2函数正在访问:" << building->m_SittingRoom << endl;
//cout << "好朋友2的visit2函数正在访问:" << building->m_BathRoom << endl;
}
void test01()
{
GoodGay gay;
gay.visit();
gay.visit2();
}
更多推荐
所有评论(0)