一、成员变量与成员函数是分开存储的。
在C++中 我们类内的成员变量和成员函数是分开存储的,只有非静态的成员变量才属于类的对象上。
#include <iostream>
using namespace std;
class Person
{
public:
//栈区
int m_a; //非静态成员变量 属于对象上
int m_c; //非静态成员变量 属于对象上
//在全局区,不属于对象上。
static int m_b; //静态成员变量,不属于这个对象上
//都在代码区 ,不属于对象上
void func()//非静态成员函数 不属于对象上
{
}
static void func2()//静态成员函数 不属于对象上
{
}
};
int main()
{
Person p;
cout << "sizeof p= " << sizeof(p) << endl;
return 0;
}
二、this指针的概念
通过第一点我们可以知道,在C++中成员变量与成员函数是分开存储的,
静态成员函数、非静态成员函数在代码区,这些函数只会诞生一份函数实例,所以同一个类型的对象都是公用这个代码的。
那么问题来了:这一块代码是如何区分是那个对象调用自己呢?
class person{
}
person p1;
//p1,就是对象,也就是说 this指向p1,那么*this就是p1本身
C++通过提供一个特殊的对象指针来解决上述的问题吗,this指针就是指向被调用的成员函数所属的对象
this指针是隐含在每一个非静态成员函数内的一种指针。(只有非静态函数才有,静态函数可以直接使用类名调用)
this指针不需要定义,直接使用即可
this指针的用途:
1 )形参和成员变量同名时,可以使用this指针加以区分
2)在类的非静态成员函数中返回对象的本身
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age) //构造函数,非静态成员函数、内部有一个this指针,当前就是在指向p1
{
this->age = age;
}
Person& PersonAddage(Person &p)
{
this->age += p.age;
//应为this是指向p4的所以想要得到p4,只需要返回this
//当你需要返回对象本身时要使用“ *this ” 和 引用“Person&”
return *this;
}
int age;
};
int main()
{
//1.使用this指针解决名字冲突问题
Person p1(18);
cout << "p1的年龄" << p1.age << endl;
Person p2(28);
cout << "p2的年龄" << p2.age << endl;
//2.返回对象的本身用*this
Person p3(10);
Person p4(10);
p4.PersonAddage(p3);
cout << "p4的年龄" << p4.age << endl;
//链式编程思想(注意一定要以引用来返回)
p4.PersonAddage(p3).PersonAddage(p3).PersonAddage(p3).PersonAddage(p3);
cout << "p4的年龄" << p4.age << endl;
return 0;
}
结论:当你需要返回对象本身时要使用“ *this ” 和 引用“Person&”
三、空指针访问成员函数
C++中的空指针也是可以调用成员函数的,但是要注意的是有没有使用到this指针
#include <iostream>
using namespace std;
class Person
{
public:
void showClassName() //非静态的成员函数 没有用到this指针
{
cout << "555" << endl;
}
void showPersonage()
{
if (this == NULL)
{
return;
}
cout << m_age << endl;
}
int m_age;
};
int main()
{
Person *p = NULL;
//虽然p指向空了,但是showClassName已经存放到代码去了,所以可以访问
p->showClassName();
//发生段错误, p指向NULL,m_age就是空了,所以会段错误
p->showPersonage();
return 0;
}
四、const修饰成员函数
常函数:
成员函数后面加const,我们就称这个函数为常函数。
特点:常函数内不可以修改成员属性。
成员属性声明时,如果加上关键词mutable后。在常函数中依然可以修改、
常对象:
声明一个对象前加const,该对象就称为常对象
常对象只能调用常函数。
#include <iostream>
using namespace std;
class Person
{
public:
//有参构造
Person(int a)
{
this->m_a = a;
}
//常函数
void showPerson() const
{
// this->m_a=100;//不可以修改成员属性
this->m_b=200; //加上butable可以赋值
}
void func()
{
}
int m_a;
mutable int m_b;
};
int main()
{
const Person p(100);//p就是一个常对象
p.showPerson();//常对象可以调用常函数
// p.func();//常对象不能调用常函数
return 0;
}
五、友元
在生活中你的家有客厅(public),有卧室(private)
客厅中所有客人都可以进去,但是你的卧室只有你才能进去,但是你可以允许你的好朋友进去
在程序中,我们有一些私有的属性也想让类外特殊的一些函数或者是类进行访问 ,这就需要用到友元了。
友元的目的,就是让一个函数或是一个类访问另一个类中的私有成员
友元的关键字:friend
有缘的三种关系:
① 全局函数做友元
②类做友元
③成员函数做友元
六、全局函数做友元
#include <iostream>
using namespace std;
//设计一个建筑物了i
class Building
{
friend void godgay(Building &b);
public:
Building()
{
m_sitroom = "keting";
m_badroom = "woshi";
}
string m_sitroom;
private:
string m_badroom;
};
void godgay(Building &b)
{
//公共权限的客厅可以访问
cout << "gay来了" << b.m_sitroom << endl;
//私有权限的我是不能访问
// cout << "gay来了" <<b.m_badroom<< endl;
//如果想让他能够访问私有属性,就必须提前告诉我们的类,
//将godgay的函数头拷贝到类中,在前面加上“friend”,后面加上”:“,就能够访问了
cout << "gay来了" << b.m_badroom << endl;
}
int main()
{
//通过建筑物类实例化一个具体的房子
Building b;
godgay(b);
return 0;
}
七、类做友元
#include <iostream>
using namespace std;
//设计一个建筑物了i
class Building
{
friend class godgay; //高速Building类 godgay是你的好朋友(友元)
public:
Building()
{
m_sitroom = "keting";
m_badroom = "woshi";
}
string m_sitroom;
private:
string m_badroom;
};
class godgay
{
public:
void visit() //尝试去访问房子中的东西
{
cout << b.m_sitroom << endl;
cout << b.m_badroom << endl;
}
Building b;
};
int main()
{
godgay g;
g.visit();
return 0;
}
八、成员函数做友元
#include <iostream>
using namespace std;
class godgay;
class Building;
class godgay
{
public:
godgay();
void visit1(); //将visit1作为building的友元
void visit2(); //不做任何设置
Building *b;
};
//设计一个建筑物了i
class Building
{
friend void godgay::visit2(); //高速Building类 godgay是你的好朋友(友元)
public:
Building()
{
m_sitroom = "keting";
m_badroom = "woshi";
}
string m_sitroom;
private:
string m_badroom;
};
godgay::godgay()
{
b = new Building;
}
void godgay::visit1()
{
cout << "godgay类中的visit正在访问" << b->m_sitroom << endl;
}
void godgay::visit2()
{
cout << "godgay类中的visit正在访问" << b->m_badroom << endl;
}
int main()
{
godgay g;
g.visit1();
g.visit2();
return 0;
}
九、运算符重载
什么是运算符重载:
对已有的运算符重载进行定义,赋予其另外一种功能。以适应不同的数据类型
1)加号运算符重载
2)左移运算符重载
3)递增运算符重载
4)赋值运算符重载
5)关系运算符重载
6)函数调用运算符重载
①加号运算符重载
#include <iostream>
using namespace std;
//加号运算符重载
class Person{
public:
//1. 使用成员函数来重载"+"号。
/*
Person operator+(Person &p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp; //返回值类型不能写Person&,因为不能返回局部变量的引用。
}
*/
int m_A;
int m_B;
};
//2. 使用全局函数来重载+号。
Person operator+(Person &p1,Person &p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
//函数重载版本
Person operator+(Person &p1,int num)
{
Person temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
int main()
{
Person p1;
p1.m_A = 10;
p1.m_B = 20;
Person p2;
p2.m_A = 30;
p2.m_B = 40;
//第一种调用方式:
//Person p3 = p2.operator+(p1); //成员函数才可以使用。
//第二种调用方式:
//Person p3 = p1 + p2; //成员函数与全局函数都可以使用。
//第三种调用方式:
Person p3 = operator+(p1,p2); //全局函数才可以使用。
//Person p3 = operator+(p1,20);
cout << "p3的m_A = " << p3.m_A << endl;
cout << "p3的m_B = " << p3.m_B << endl;
return 0;
}
②左移运算符重载
#include <iostream>
using namespace std;
//左移运算符重载
class Person
{
public:
Person(int a, int b)
{
this->m_A = a;
this->m_B = b;
}
int m_A;
int m_B;
};
//使用全局函数来重载左移运算符<<
ostream &operator<<(ostream &cout, Person &p)
{
cout << "m_A = " << p.m_A << " m_B = " << p.m_B;
return cout;
}
int main()
{
//通过Person类,实例化一个对象。
Person p(10, 20);
//1. 直接输入里面的值,不需要重载也是可以的。
//cout << "p.m_A = " << p.m_A << endl;
//cout << "p.m_B = " << p.m_B << endl;
//2. 如果想cout Person类型的对象,就必须要重载<<
//第一种使用方式:
//cout << p; //m_A = 10 m_B = 20
//第二种使用方式:
//operator<<(cout,p);
//3. 如果输出完一个东西之后,再继续输出另外一个东西,就必须将cout的本体返回。
//cout << p << "helloworld" << endl;
return 0;
}
③递增运算符重载
#include <iostream>
using namespace std;
//重载递增运算符
//设计整型数据类
class MyInteger{
public:
MyInteger()
{
this->m_Num = 0;
}
//重载前置++运算符
//这里必须写MyInteger&,因为前置递增是支持连续递增,所以必须将对象的本体返回。
MyInteger& operator++()
{
//1. 先进行++运算。
m_Num++;
//2. 将自身返回。
return *this;
}
//重载后置++运算符
//就是因为temp是局部对象,不能以引用的方式返回,这就解析了为什么C++中不可以支持连续递增。
MyInteger operator++(int)
{
//1. 先记录当前的结果。
MyInteger temp = *this; //局部对象
//2. 递增。
m_Num++;
//3. 最后将记录的结果返回去即可。
return temp;
}
int m_Num;
};
ostream& operator<<(ostream &cout,MyInteger &myint)
{
cout << myint.m_Num;
return cout;
}
int main()
{
//1. 前置递增。
//MyInteger myint;//0 //实例化整型数据类的对象。
//++(++myint);
//cout << myint << endl;//2
//2. 后置递增。
MyInteger myint; //0
myint++; //C++不支持连续的后置递增,所以不需要考虑返回引用。
cout << myint << endl; //1
return 0;
}