成员变量和成员函数分开储存
只有非静态成员变量才属于类的对象上,静态成员变量和成员函数不属于类的对象上,即sizeof(对象),其字节大小只包括非静态成员变量
空对象占用的内存空间为:1个字节;C++编译器会给每个空对象分配一个字节的内存空间,是为区分空对象占内存的位置。
this指针的本质是指针常量,其指向不可以修改,但是指向的值可以修改。
指向被调用的成员函数所属的对象
隐含在每一个非静态成员函数内,不用程序员手动去写
不需要定义,直接使用即可
使用场景:
class Point
{
public:
int x,y;
//场景一:当形参和成员变量同名是,可用this指针区分
//this是指向当前对象的指针
Point(int x, int y)
{
this->x = x;
this->y = y;
}
//场景二:在类的用非静态成员函数中用于返回对象本身,可用return *this
//链式编程思想
Point& swap()
{
x += 2;
y += 2;
return *this;
}
};
调用:
Point p2 = p1.swap().swap().swap();
注意:
C++中空指针可以调用成员函数,需注意如果用到this指针(即访问到了非静态成员变量),需要加以判断是否是空指针来保证代码的健壮性。
常函数:
示例:
int x;
mutable int y;
void showPoint() const
{
this = NULL;//错误,this指针本质是常量指针,指向不可以修改;
this->x = 0;//错误,常函数类似与const Point *const this;this指针指向的值也不可以修改
this->y = 0;//正确
}
常对象:
示例:
const Point p;
p.x = 0;//出错,常对象的成员变量不可以修改
p.y = 0;//mutable声明的成员变量可以修改
p.swap();//出错,不允许调用成员函数
p.showPoint();//正确
友元的目的:让一个函数或者一个类访问另一个类中的私有成员
使用friend关键字
友元的三种场景:
在类中进行全局函数的友元声明:(不需要用public等权限说明,直接放到类中即可),使得该全局函数可以访问该类(Point)的私有成员
friend void goodGay(Point &p);
class goodGay
{
public:
Point *p; //声明一个Point对象,以访问该类的成员
};
在Point中声明友元类,表名goodGay是本类的友元,该类的中的成员可以访问本类的私有成员
friend class goodGay;
class goodGay
{
public:
Point *p; //声明一个Point对象,以访问该类的成员
void visit(); //让visit函数可以访问Point中的私有成员
};
在Point中声明友元函数,表名visit函数是本类的友元,该成员函数可以访问本类的私有成员
friend void goodGay::visit();
运算符重载的含义是对于非内置的类让其也可以通过运算符进行运算。但内置的数据类型的表达式的运算符是不可以改变的。
可以成员函数,全局函数进行重载,下面是一个复数相加的"+"成员函数重载的示例。
#include
using namespace std;
class Complex{
friend ostream& operator<<(ostream &cout,Complex c);
private:
int m_real, m_imag;
public:
Complex(int real, int imag):m_real(real), m_imag(imag)
{
}
// 重载"+"运算符
Complex& operator+(Complex& c)
{
m_real += c.m_real;
m_imag += c.m_imag;
return *this;
}
//重载前置递增运算符 ,返回引用是为了对一个数据进行操作
Complex& operator++()
{
m_real++;
return *this;
}
//重载后置递增运算符 ,后置递增要返回值,因为不能返回局部变量的引用
Complex operator++(int)
{
Complex temp = *this;
m_real++;
return temp;
}
};
//重载左移运算符
ostream& operator<<(ostream &cout,Complex c)
{
cout<
运算符重载也可以发生函数重载,即可以不和同类对象相加,重载函数参数可以是其他类型对象
作用:可用于输出自定义的数据类型
一般不会在自定义的类中利用成员函数来重载左移运算符,因为无法实现cout在左侧
ostream& operator<<(ostream &cout,Complex &c)
{//cout属于ostream类对象,输出流对象
cout<
调用:
cout<
C++编译器默认给一个类添加赋值运算符,对属性进行值拷贝;
与复制构造函数一样,值拷贝也有出现浅拷贝问题,这时我们需要自己定义赋值运算符重载进行深拷贝,来解决这个问题。
以下成员函数接上面复制构造函数的例子:
Point& operator=(Point &p)
{
if(p_z)
{
delete p_z;
p_z = NULL;
}
p_x = p.p_x; p_y = p.p_y;
p_z = new int(*(p.p_z));
return *this;
}
bool operator==(Complex &c)
{
if(m_real==c.m_real && m_imag==c.m_imag) return true;
else return false;
}
示例:
在类中定义成员函数:
void operator()()
{
cout<<"函数调用运算符重载"<
调用:
c(); //c为一个类对象
Complex()();//匿名函数对象,Complex()为一个匿名对象
继承是面向对象的三大特性之一
用继承的技术,子类继承父类中的成员,可减少重复代码
子类中的成员,包含两部分:从父类继承过来的成员,以及自己增加的成员
语法:
class 子类B(派生类):继承方式 父类A(基类)
{
仅包含B-A的部分
}
三种继承方式:
父类中私有成员,子类不论用什么继承方式均不能访问
sizeof(子类) = sizeof(父类) + 自己新增的
父类所有权限非静态成员都会在子类中包含一份,只是因为权限问题可能访问不到
创建一个子类对象,也需要创建一个父类对象
顺序:
子类访问子类中同名成员,直接访问即可
子类访问父类中的同名成员,需要添加作用域
如果子类中有和父类同名的成员函数,子类的同名成员函数会隐藏掉父类中的所有同名成员函数,包括不同的重载函数
Son s;
s.m_A = 10;//访问子类的成员变量
s.Father::m_A = 10;//访问父类的成员变量
s.func();//访问子类的成员函数
s.Father::func();//访问子类的成员函数
继承中同名静态成员的处理方法:
class 子类(派生类):继承方式 父类1,继承方式 父类2....
实际开发中不建议使用
概念:
问题:
D类同时继承了B类和C类,同名成员存在二义性的问题
D类中包含了2份A类中的数据,怎么处理
加上visual关键字,利用虚继承,可以解决菱形继承的问题,同名成员就只有一份,调用时不用加作用域;
A称为虚基类
class B:visual public A{};
class C:visual public A{};
class D:public B, public C{};
vbptr —虚基类指针,指向vbtable,vbtable中记录一个偏移量,通过该偏移量找到成员变量