1.内存
分四区(意义:对不同区域的数据,赋予不同的生命周期,给我们更大的灵活编程)
(1)代码区:存放二进制代码,由操作系统管理
(2)全局区:存放全局变量、静态变量、常量(该区数据由系统释放)
(3)堆区:由人分配、释放。若人没手动释放,则结束时会由操作系统回收(用new在堆区开辟内存,用delete释放)
new返回的是该类型数据的指针,如int* p = new int(10);
(4)栈区:由编译器自动分配、释放,存放函数参数、局部变量等
2.引用
作用:用来给变量起别名
数据类型 &别名 = 原名;
(1)用引用,必须要初始化,且之后就不能更改了
以上输出的a、b、c都是20 。
(2)引用做函数参数
作用:函数传参时,可以用引用的技术让形参修饰实参
优势:与地址传递的效果一样,其更好理解。
(3)引用做函数的返回值
(4)引用的本质是指针常量(指针的指向不可改,指向的值可改)
(5)常量引用
3.函数
(1)默认参数
(2)占位参数
(3)函数重载
作用:函数名可以相同,提高复用性
需满足3个条件:
①同一个作用域
②函数名称相同
③参数类型不同或个数不同或顺序不同
(函数返回值不能作为函数重载的条件)
函数重载注意事项:
c++认为万事万物皆为对象,有属性与行为。
二者关系:把具有相同性质的对象,可以抽象称之为类
(1)封装的意义:类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有3种:
public 公共权限,成员类内外都可访问;
protected 保护权限,成员类内可访问(同一个class内),类外不行,儿子(如A:public B)可以访问父亲中的保护内容;
private 私有权限,成员类内可访问,类外不行,儿子不能访问父亲中的保护内容;
注:
C++ 访问权限问题主要包括两种:一种是外界(类外)对类成员的普通访问,通过类内的public;另一种是继承关系中子类对父类成员的访问。
基本原则:外界只能访问类中public成员,子类可访问父类的public和protected成员;不同继承方式只影响外界(包括子类的子类)通过子类对父类成员的访问权限。
一、 外界对类成员的普通访问
1. 当类成员在类中为public权限时,才可在类外访问,其余不可。
2. 若存在继承关系,从父类继承来的成员若在该类中仍具有public权限,也可在类外访问,其余不可。
二、继承关系中子类对父类成员的访问
1. 无论通过什么方式(public、protected、private)继承,在子类内部均可访问父类中的public、protected成员,private成员不可访问。
注意:
继承方式只影响外界(包括子类的子类)通过子类对父类成员的访问权限。
public继承,父类成员的访问权限全部保留至子类;
protected继承,父类public成员的访问权限在子类中降至protected;
private继承,父类public、protected成员的访问权限在子类中均降至private。
2. 父类的析构函数若声明为protected (无论有无virutal),外界均不可调用delete 父类指针;因为是protected权限,子类析构后会自动调用父类析构函数。
这种情况下,最好不要在父类成员中有动态内存分配。
3. 通过protected/private继承的子类,不能通过static_cast/dynamic_cast向父类转换;只能通过reinterpret_cast引用或指针的方式强制转换,按照父类内存结构重新解释,可改变成员的访问权限。
举例
可见以上目前存在私有的访问权限问题,解决方法有两种:
① 改为成员函数,父类的成员函数是可以访问自己的私有成员的
② 改为友元函数,可以在类外访问私有变量
(2)同样表示的是类,struct与class唯一的区别在于默认的访问权限不同:struct默认公有,class默认私有
(3)成员属性设置为私有的好处:可以自己控制读写权限。且对于写权限,我们可以检测数据的有效性。
再通过类内设置的get()、set()方法来读取或改写私有属性。
例
#include
using namespace std;
//创建立方体类
//设计属性
//设计行为 获取立方体面积和体积
//分别利用全局函数和成员函数 判断两个立方体是否相同
class Cube
{
int m_L;
int m_W;
int m_H;
public:
void setL(int l) //设置长
{
m_L = l;
}
int getL() //获取长
{
return m_L;
}
void setW(int w)
{
m_W = w;
}
int getW()
{
return m_W;
}
void setH(int h)
{
m_H = h;
}
int getH()
{
return m_H;
}
int calculateS()
{
return 2*(m_L*m_W + m_L*m_H + m_W*m_H);
}
int calculateV()
{
return m_L*m_W*m_H;
}
//用成员函数判断两个立方体是否相同
bool isSameByClass(Cube &c)
{
if(m_L==c.getL() && m_W==c.getW() && m_H==c.getH())
return true;
return false;
}
};
bool isSame(Cube &c1, Cube &c2);
int main()
{
Cube c1;
c1.setL(10);
c1.setW(10);
c1.setH(10);
cout << "c1面积: " << c1.calculateS() << endl;
cout << "c1体积: " << c1.calculateV() << endl;
Cube c2;
c2.setL(10);
c2.setW(10);
c2.setH(11);
if(isSame(c1, c2))
cout << "全局函数判断: c1和c2相同 \n";
else
cout << "全局函数判断: c1和c2不同 \n";
if(c1.isSameByClass(c2))
cout << "成员函数判断: c1和c2相同 \n";
else
cout << "成员函数判断: c1和c2不同 \n";
return 0;
}
//用全局函数判断两个立方体是否相同
bool isSame(Cube &c1, Cube &c2)
{
if(c1.getL()==c2.getL() && c1.getW()==c2.getW() && c1.getH()==c2.getH())
return true;
return false;
}
例
#include
using namespace std;
//点类
class Point
{
int m_X;
int m_Y;
public:
void setX(int x)
{
m_X = x;
}
int getX()
{
return m_X;
}
void setY(int y)
{
m_Y = y;
}
int getY()
{
return m_Y;
}
};
class Circle
{
int m_R;
Point m_Center;
public:
void setR(int r)
{
m_R = r;
}
int getR()
{
return m_R;
}
void setCenter(Point center)
{
m_Center = center;
}
Point getCenter()
{
return m_Center;
}
};
//判断点与圆的关系
void isInCircle(Circle &c, Point &p)
{
//c.getCenter()返回的m_Center是Point类,所以可以直接调用Point的getX()、getY()方法。
int Distance =
(c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX())
+ (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
int rDistance = c.getR() * c.getR();
if(Distance == rDistance)
cout << "点在圆上\n";
else if (Distance > rDistance)
cout << "点在圆外\n";
else
cout << "点在圆内\n";
}
int main()
{
Circle c;
c.setR(10);
Point center;
center.setX(10);
center.setY(0);
c.setCenter(center);
Point p;
p.setX(10);
p.setY(11);
isInCircle(c, p);
return 0;
}
(4)构造函数用于初始化,析构函数用于清理
以上能说明,在调用对象时会自动地调用构造与析构函数。
(5)构造函数分类及调用
① 构造函数分类
② 构造函数调用
函数调用的3种方法:
(6)拷贝构造函数调用时机
(7)构造函数的调用规则
默认情况下,c++的每个类至少包含3个函数,构造、析构和拷贝构造函数。
(8)深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝
深拷贝:在·堆区重新申请空间,做拷贝操作
注:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,以防止浅拷贝带来的问题。
(9)初始化列表
(10)类对象做为类成员
类里的成员可以是另一个类的对象,称此成员为对象成员
(11)静态成员:变量,函数
静态成员函数只能访问静态成员变量:
静态成员变量有两种访问方式:
(12)类内的成员变量与成员函数是分开存储的,只有非静态成员变量才属于类的对象上。
c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置,每个空对象都具有独一无二的内存地址。
(13)this指针!!!(和&引用一样,都是指向不可变)
特殊的对象指针,用来指向被调用的成员函数所属的对象。本质是指针常量,其指向不可改。
如上图,因为this是指针,其指向对象p1,则解引用*this得到p1,即返回了对象本身。
作用:
①解决名称冲突(当形参和成员变量同名时,可以用this指针做区分)
②返回对象本身用*this
例
可见并没有完成赋值,原因就是形参和成员变量名重复了,此时就可以用this指针解决:
(14)空指针访问成员函数
报错如下:
修改如下,以保证代码健壮性:
(15)常函数与常对象!!!
成员函数之后加const,称为常函数,其内不可修改成员属性。但当成员属性在声明时加了关键字mutable后,则可修改。
声明对象前加const,称为常对象,常对象只能调用常函数。
注:在成员函数后加const,修饰的是this指针,令其指向的值(this指针作为指针常量,其指向不可改,指向的值可改)也不可能改了。
注:
const char* ptr == char const* ptr; (可以直接改变指针指向,但不能直接改变指针指向的值);*ptr=*ss;
char* const ptr; (可以直接改变指针指向的值,但不能直接改变指针指向);ptr[0]='s';
但两者都可以通过改变所指向的指针的内容,来改变它的值。
const char* const ptr;(指向和指向的值都不可改)。
例1
例2
例3
(16)友元 friend
让类外的一些函数或类,能够访问该类的私有成员!!!
3种实现:全局函数做友元,类做友元,成员函数做友元
1)全局函数做友元
2)类做友元
3)成员函数做友元
(17)运算符重载
对已有的运算符做重新定义,赋予其另一种功能,以适应不同的数据类型
1)加号运算符重载:用成员函数实现,用全局函数实现
实现两个自定义数据类型相加
①用成员函数实现
②用全局函数实现
③重载运算符函数的重载
2)左移运算符重载
重载左移运算符配合友元,可以实现输出自定义数据类型
3)递增运算符重载
4)赋值运算符重载
5)关系运算符重载
6)函数调用运算符重载
函数调用运算符()也可以重载。由于重载后使用的方式非常像函数的调用,故称为仿函数。
仿函数没有固定写法,非常灵活。
(1)继承的方式
例
(2)继承中的对象模型
(3)继承中的构造与析构顺序
(4)继承中,同名成员的处理方式
(5)继承中,同名静态成员的处理方式
(6)多继承
(3)菱形继承
菱形继承带来的问题是子类会同时继承两份相同的数据,导致资源浪费以及毫无意义。可以用虚继承来解决这个问题。
(1)多态的基本语法
如果想执行让猫说话,那么这个函数地址就不能提前绑定,而是要在运行阶段才绑定,要借助虚函数来实现:
注:重写不是重载。重写是指函数的返回值类型、函数名、参数列表都完全一致
(2)
(3)纯虚函数与抽象类
举例:
(4)虚析构与纯虚析构
举例: