成员变量和函数的存储
c++实现了封装,数据和数据处理的操作是分开存储的,c++中非静态数据成员直接内含在类对象中,成员函数虽然内含在class声明之内,却不在对象之中。每一个非内联函数只会诞生一份函数实例。
我们可以用sizeof来测量一个类的大小理解它的空间是如何计算的:
但是我们要知道sizeof测的是这个类将来实例化对象后,为对象开辟的空间大小。
#include<iostream> using namespace std; class test0 { public: int a; }; class test1 { int a; static int b; }; class test2 { int a; static int b; void printtest() { cout << "hello world" << endl; } }; class test3 { }; class test4//综合计算 { public: int a=0;//普通的成员变量 static int b;//静态成员不存在类实例化的对象中 void show()//普通成员函数不存在类实例化的对象中 { cout << a << " " << b << endl; } static void show1()//静态成员函数 不存在类实例化的对象中 { cout << b << endl; } }; int test4::b = 1; void test01() { test4 p; p.show(); //空类的大小不是0 而是1 cout << sizeof(test0) << endl; cout << sizeof(test1) << endl; cout << sizeof(test2) << endl; cout << sizeof(test3) << endl; cout << sizeof(test4) << endl; cout << sizeof(p) << endl; } int main() { test01(); }
故对于类对象成员中空间的占用为:
变量:
类对象成员-普通成员变量占用对象空间大小
类对象成员-静态成员变量不占用对象空间大小
函数:
类对象成员-普通成员函数不占用对象空间大小
类对象成员-静态成员函数不占用对象空间大小
this指针
通过上述我们知道。c++的数据和操作其实是分开的存储,并且每一个非内联成员函数指挥诞生一份函数实例,也就是多个同类型的对象会共用一块代码,问题是这一快代码是如何区分那个对象在调用该函数,这里会引入this指针(一个对象指针),来决定调用。
this指针的工作原理
类的成员函数默认编译器都会加上了一个this指针,这个this指针 指向调用该成员函数的对象。
class num { public: int ma; void seta(int x) { ma = x; cout << ma << endl; } }; int main() { num s1; s1.seta(20); num s2; num s3; }
this指针是实例化对象后编译器就有的,调用的时候也是编译器的自动调用,适隐藏的功能。
对于成员函数,就是通过this指针解决是哪一个对象调用的问题,this指针无需定义,可以直接使用。对于静态函数成员是不存在this指针的,静态成员函数是不能操作非静态变量的。
this指针的应用
class person { public: person(int age, string name)// this { this-> age = age; this-> name = name; } void show() { cout << age << " " << name << endl; } person person_add(person & p2)//this ‐‐‐‐‐‐> p1 函数类型为类的函数 { person p(this-> age + p2.age, this-> name + p2.name);//"helloworld" return p;//返回调用该成员函数的对象地址,即this指针 } int age; string name; }; person person_add(person & p1, person & p2) { person p(p1.age + p2.age, p1.name + p2.name);//"helloworld" return p; } void test02() { person p1(10, "hello"); person p2(20, "world"); //p3 = p1 + p2 30,"helloworld" //person p3 = person_add(p1,p2); //p3.show(); person p3 = p1.person_add(p2); p3.show(); } void test01() { person p1(10, "lucy"); p1.show(); } int main() { test02(); return 0; }
可以看到我们可以通过函数返回this指针来使用某个实例化的对象。对象调用函数,函数返回this指针。
const修饰的成员函数
用const修饰成员函数时,const修饰this指针指向的内存区域,即该指针只读,对象内部不可被修改,即类中的任何普通成员变量不可被修改。成员函数体内不可以修改本类中的任何普通成员变量,当成员变量类型符前用mutable修饰时例外。
这个const修饰的是指针 const type * const this,代表不能 通过this指针去修改this指针指向对象的内容,即类中的任何普通成员变量。
person person_add(person & p2)const//const person * const this ‐‐‐‐‐‐> p { //this‐>age = 200; person p(this‐ > age + p2.age, this‐ > name + p2.name);//"helloworld" return p; }
友元
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用 域之外)访问。但是,有时候需要在类的外部访问类的私有成员,怎么 办?
解决方法是使用友元函数, 友元函数是一种特权函数,c++允许这个特 权函数访问私有成员。
这一点从现实生活中也可以很好的理解: 比如你的家,有客厅,有你的卧室,那么你的客厅是Public 的,所有来 的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去, 但是呢,你也可以允许你的闺蜜好基友进去。
如果想要让全局函数或一个类的成员函数访问另一个类私有成 员,只需要声明友元即可。
友元的语法
使用friend关键字声明友元,friend只要出现在声明处,一个函数或者类作为另一个类的的友元,那么这个函数或类就可以直接访问另一个类的私有数据。
友元重要运用在运算符重载上。
1.普通全局函数成为类的友元
#include<string> class Room { friend void visit1(Room& room);//声明一个友元函数,且为普通全局函数 private: string bedroom; public: string setingroom; public: Room(string bedroom, string setingroom) { this->bedroom = bedroom; this->setingroom = setingroom; } }; //普通全局变量 void visit1(Room &room) { cout << "访问了" << room.bedroom << endl;//可以访问了 cout << "访问了" << room.setingroom << endl; } int main() { Room room("卧室", "客厅"); visit1(room);//直接调用 return 0; }
2.类的某个成员函数作为另一个类的友元
举例如下:
#include<string> class Room;//声明类,但是只说明类名称仅此而已 class godgay { public: void visiting1(Room& room); void visiting2(Room& room); }; class Room { friend void godgay::visiting2(Room& room);//声明另一个类的函数为该类的友元函数 private: string bedroom; public: string setingroom; public: Room(string bedroom, string setingroom) { this->bedroom = bedroom; this->setingroom = setingroom; } }; void godgay::visiting1(Room& room) { cout << "客人访问问了" << room.setingroom << endl; //cout << "访问了" << room.bedroom << endl;访问不了 } void godgay::visiting2(Room& room) { cout << "好基友访问了" << room.bedroom << endl; } int main() { Room room("卧室", "客厅"); godgay a; a.visiting2(room);//直接调用 a.visiting1(room); return 0; }
我们将一个类中的函数声明为另一个类中的友元函数,该友元函数便可以访问该类中的私有数据。
在这里我们需要注意类的声明,类的声明只是说有个这个名字的类,但此时类的成员未定义,引用会报错,我们需要再定义类,之后再访问。
整个类作为另一个类的友元
class Building; class Goodgay { public: Goodgay(string hall, string bedroom); void visit(); Building * b; }; class Building { //friend void print_Building(Building &b); //friend class Goodgay; //一个类成为另一个类的友元 friend void Goodgay::visit();//类的成员函数成为另一类的友元 public: Building(string hall, string bedroom) { this-> bedroom = bedroom; this-> hall = hall; } string hall; private: string bedroom; }; Goodgay::Goodgay(string hall, string bedroom) { b = new Building(hall, bedroom); } void Goodgay::visit() { cout << b-> hall << " " << b-> bedroom << endl; } void test01() { Goodgay gd("卧龙山庄", "闺房"); gd.visit(); } int main() { test01(); return 0; }
将一个类作为友元,即类中的任何成员都可以访问该私有数据。
注意事项:
1.友元关系不能被继承
2.友元关系是单向性的
3.友元关系不具有传递性
运算符重载
1 运算符重载的基本概念
运算符重载: 就是给运算符赋予一个新的意义
int a =1;
int b=2;'
int c = a +b;
类相加:
person p1;
person p2;
person p3= p1+p2;
运算符只能运算内置的数据类型,对于自定义的数据类型,不能运算,所以
我们可以重载运算符。
2 重载加号运算符
class person { public: person(int age) { this-> age = age; } person operator+(person & p2) { person p(this->age + p2.age); return p; } int age; }; //person operator+(person &p1, person &p2) //{ // person p(p1.age+p2.age); // return p; //} void test01() { person p1(10); person p2(20); person p3 = p1 + p2;// operator+(p1,p2) p1.operator+(p2) cout << p3.age << endl; } int main() { test01(); return 0; }
在这里我们也可以自己定义一个operator+的运算符,可以将两个类中的age相加。
3 重载左移运算符和算符重载碰上友元函数
class person { friend ostream & operator<<(ostream & cout, person & p); public: person(int age) { this-> age = age; } private: int age; }; ostream & operator<<(ostream & cout, person & p) { cout << p.age; return cout; } void test01() { person p1(10); cout << p1 << endl; // operator<<(cout,p1) //cout.operator<<(p1) 重载左移运算符 } int main() { test01(); return 0; }
这里的ostream&opeator<<是一个插入格式化输出函数。
可以重载的运算符
几乎 C 中所有的运算符都可以重载,但运算符重载的使用时相当受限制 的。特别是不能使用C 中当前没有意义的运算符 ( 例如用 ** 求幂 ) 不能改变 运算符优先级,不能改变运算符的参数个数。这样的限制有意义,否 则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。