4.2.8静态成员
静态成员变量
1.所有对象共享同一份数据;
2.在编译阶段分配内存;
3.类内声明,类外初始化;
示例:
class Person
{
public:
public:
static int _A;
//静态成员变量,不属于某个对象上,所有对象都共享同一个数据
//静态成员变量具有两种访问方式
//1.通过对象进行访问
//2.通过类名进行访问
//静态成员变量也是有访问权限的
private:
static int _B;//类内声明
};
int Person::_A=100;
int Person::_B = 200;//类外初始化
void test02()
{
//通过类名访问静态成员变量
cout << Person::_A << endl;
//cout << Person::_B << endl;类外访问不到私有(保护)静态成员变量
}
静态成员函数
1.所有对象共享同一个函数;
2.静态成员函数只能访问静态成员变量;
示例:
class Person
{
public:
static void func()//静态成员函数也是有访问权限的
{
_A = 100;//静态成员函数可以访问 静态成员变量
//_B = 200;//静态成员函数不可以访问 非静态成员变量
//因为无法区分到底是哪个对象的_B
cout << "static void func()" << endl;
}
public:
static int _A;
int _B;
private:
static void func2()
{
cout << "static void func2()" << endl;
}
};
int Person::_A = 0;
void test01()
{
Person p;
p.func();//1.通过对象访问
cout << "-------" << endl;
Person::func();//2.通过类名访问
}
void test02()
{
//Person::func2();//类外访问不到私有的静态成员函数
}
4.3C++对象模型和this指针
4.3.1成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
4.3.1this指针
每个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
C++通过提供特殊的对象指针,this指针,能够使得代码区分调用自己的对象
概念:
1.this指针指向被调用的成员函数所属的对象;
2.this指针是隐含每一个非静态成员函数的一种指针;
3.this指针不需要定义,直接使用即可;
用途:
1.当形参和成员变量同名时,可用this指针来区分;(解决名称冲突)
2.在类的非静态成员函数中返回对象本身,可使用 return *this;
示例:
class Person
{
public:
Person(int age)
{
//1.解决名称冲突
//age = age;//error ,编译器认为两个age是同一个,即形参age
//this指针指向的是被调用成员函数所属的对象
this->age = age;
}
void PersonAddAge(Person &p)
{
this->age += p.age;
}
//易错点
//Person PersonAddAge2(Person &p)
//error
Person & PersonAddAge2(Person &p)
{
this->age += p.age;
//this是指向p2的指针,而*this就是p2这个对象本体
return *this;
}
Person PersonAddAge3(Person &p)
{
this->age += p.age;
//this是指向p2的指针,而*this就是p2这个对象本体
return *this;
}
int age;
};
void test01()
{
Person p1(18);
cout << "the age of p1 is " << p1.age << endl;
}
//2.返回对象本身用*this
void test02()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge(p1);
cout << "the age of p2 is " << p2.age << endl;
//p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
//error
}
void test03()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge2(p1);
cout << "the age of p2 is " << p2.age << endl;
//链式编程思想
p2.PersonAddAge2(p1).PersonAddAge2(p1).PersonAddAge2(p1);
cout << "the age of p2 is " << p2.age << endl;
}
void test04()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge3(p1);//p2.age更改,但函数返回的是p2的拷贝
//此时p2.age=20
cout << "the age of p2 is " << p2.age << endl;
//第一个函数更改了p2.age 但返回的是p2的拷贝 记为 p2’
//第二个函数更改的是p2’
//第三个函数更改的是p2''
//所以p2.age=30
p2.PersonAddAge3(p1).PersonAddAge3(p1).PersonAddAge3(p1);
cout << "the age of p2 is " << p2.age << endl;
}
4.3.3空指针访问成员函数
C++中空指针也可以调用成员函数,但要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码健壮性
示例:
class Person
{
public:
//空指针可以正常访问
void showClassName()
{
cout << "this is Person class" << endl;
}
void showPersonAge()
{
//报错原因是因为传入的指针为NULL
cout << "age is " << m_Age << endl;
//m_Age默认为this->m_Age
}
void showPersonAge2()
{
//提高代码健壮性
if (this == NULL)
{
return;
}
cout << "age is " << m_Age << endl;
}
int m_Age;
};
void test01()
{
Person *p = NULL;
p->showClassName();
//p->showPersonAge();
//error
p->showPersonAge2();
}
4.3.4const修饰成员函数
常函数:
1.成员函数后加const后我们称这个函数为常函数;
2.常函数内不可以修改成员属性;
3.成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
1.声明对象前加const称该对象为常对象;
2.常对象只能调用常函数;
示例:
class Person
{
public:
void showPerson1()
{
//this指针本质是指针常量,指针的指向是不可以修改的
//Person * const this;
this->m_A = 100;//this指针指向的内容可以修改
//this = NULL;//this指针不可以修改指针指向
}
//在成员函数后面加上const 修饰的是this指针,让指针指向的值也不可以修改
void showPerson2() const //常函数
{
//如果想要this指针指向的值也不可以修改
//把const加在成员函数后
//就相当于const Person * const this;
//this->m_A = 100;//error
this->m_B = 100;//ok
}
public:
int m_A;
mutable int m_B;//特殊变量,即使在常函数中,也可以修改这个值
//加关键字mutable
};
void test01()
{
//常对象
const Person p;
//p.m_A = 100;//error
p.m_B = 100;//ok ,m_B是特殊值,在常对象下也可以修改
//常对象只能调用常函数
//不可以调用普通成员函数,因为普通成员函数可以修改属性
//p.showPerson1();//error
p.showPerson2();//ok
}
4.4友元
目的:让一个函数或类 访问另一个类中的私有成员
关键字:friend
友元的三种实现:
1.全局函数做友元函数
2.类做友元类
3.类的成员函数做友元
4.5运算符重载
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
4.5.1加号运算符重载
作用:实现两个自定义数据类型相加的运算
两种方式:
1.成员函数重载
2.全局函数重载
示例:
class Person
{
public:
//Person operator+(Person &p);//1.成员函数重载
Person(){}
Person(int a, int b);
public:
int m_A;
int m_B;
};
//Person 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::Person(int a, int b)
{
m_A = a;
m_B = 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 a)
{
Person temp;
temp.m_A = p1.m_A + a;
temp.m_B = p1.m_B + a;
return temp;
}
void test00()
{
Person p1(10, 10);
Person p2 = p1 + 10;
cout << "p2.m_A=" << p2.m_A << endl
<< "p2.m_B=" << p2.m_B << endl;
}
void test01()
{
Person p1(10, 10);
Person p2(20, 20);
Person p3 = p1 + p2;
//1.成员函数重载的本质调用
//Person p3=p1.operator+(p2);
//2.全局函数重载的本质调用
//Person p3=operator+(p1,p2);
}
总结:
1.对于内置的数据类型的表达式的运算符是不可能改变的;
2.不要滥用运算符重载;
4.5.2左移运算符重载
作用:输出自定义数据类型
注意:只能通过全局函数重载输出流运算符,且需要设置为类的友元函数
示例:
class Person
{
//将全局函数设置为友元
friend ostream & operator<<(ostream &os, Person &p);
public:
//利用成员函数重载 左移运算符 p.operator<<(cout) 简化版本 p<<cout
//不会利用成员函数重载<<运算符 ,因为无法实现cout在左侧
//void operator<<(cout ){}
Person(int a, int b)
{
m_A = a;
m_B = b;
}
private:
int m_A;
int m_B;
};
//只能利用全局函数重载左移运算符
//void operator<<(ostream &cout,Person &p)//本质 operator<<(cout,p) 简化 cout<<p
//{
// cout << "m_A=" << p.m_A << endl
// << "m_B=" << p.m_B << endl;
//}
//ostream 也是一种类
//ostream对象只能有一个,所以需要加&
ostream & operator<<(ostream &os, Person &p)//本质 operator<<(cout,p) 简化 cout<<p
{
os << "m_A=" << p.m_A <<" "
<< "m_B=" << p.m_B ;
return os;
}
void test01()
{
Person p(10,10);
//cout << p ;
cout<<p<<endl;
//返回值为void时,上一行程序会崩掉
//由于左侧返回的是void ,不能实现链式编程
//因此需要对左移运算符重载函数进行返回值的修改
}
总结:重载左移运算符配合友元可以实现输出自定义数据类型