1.概念:根据下图我们可以直观的了解类和对象
类:对现实生活中事物的描述
对象:对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。
注:ADT为抽象数据类型
2.创建:
[修饰符] class类名
{
private:
成员变量;
public:
成员方法
}
C++三大特征:封装、继承、多态
封装:举个简单通俗例子,你向朋友借钱,你应该向他发送信号说我要借钱,朋友说可以,这时应该等待朋友给你取钱,而你不能直接从朋友衣服中取钱,封装就是这样的,简单说就是该给你看的给你看,属于公有的(public),不该给你看的不能看,属于私有的(private)。继承和多态暂不讨论。
类中限定访问符:1.public:任意位置可以访问
2.protected:本类、子类中可以访问
3.private:本类类中访问
例:学生类的成员变量为:姓名、学号、年龄 成员方法为:吃饭、睡觉、打豆豆
class Student
{
public:
void eat()// Student*const this隐藏指针
{
//this = &s1;
std::cout << this->mname << " is eating" << std::endl;
std::cout << this->mage << std::endl;
}
void sleep()
{
std::cout << mname << " is sleeping" << std::endl;
}
void play()
{
std::cout << mname << " is playing doudou" << std::endl;
}
private:
char* mname;
char* mid;
int mage;
};
3.struct当成类处理
在C++中struct当成类处理,与C语言中不同的是:在类中默认访问符是私有的(private),struct中默认为公有的(public)
4.this(thiscall):类中普通的成员方法中默认的参数,this
我们使用时代码为:
void eat()
{
std::cout<<mname<<"is eating"<<std::endl;
}
实际隐藏的完整代码为:
void eat( this)
{
std::cout<<this->mname<<"is eating"<<std::endl;
}
5.成员方法在类中/外实现:
类中:默认内联函数inline
类外:普通方法(有开栈、清栈过程)
6.类中六个默认函数:分为公有的、inline
例:实体为商品,成员变量为:名字、价格、数量 成员方法:买、卖
1.构造函数 :初始化对象的内存空间,函数名和类名相同,无返回值
特点:可以重载、不能手动调用、顺序构造
系统默认:CGoods(){}
自己提供:CGoods(char* name, float price, int amount)
{
std::cout << this << " :CGoods::CGoods(char*,float,int)" << std::endl;
mname = new char[strlen(name) + 1]();
strcpy(mname, name);
mprice = price;
mamount = amount;
}
调用点:CGoods good("car",10.1,10);//初始化对象内存
2.析构函数:释放对象所占的其他资源
特点:不可重载、可以手动调用,即退化成普通函数的调用、先构造的后析构
~CGoods()
{
std::cout << this << " :CGoods::~CGoods()" << std::endl;
delete[] mname;
}
3.拷贝构造函数:用已存在的对象来生成一个相同类型的新对象
特点:形参用&接收、防止递归构造形参对象导致栈溢出
CGoods(const CGoods& rhs)
{
std::cout << this << " :CGoods::CGoods(const CGoods&)" << std::endl;
mname = new char[strlen(rhs.mname) + 1]();
strcpy(mname, rhs.mname);
mprice = rhs.mprice;
mamount = rhs.mamount;
}
调用点: CGood good1("car1",10,1,10);
CGood good2=goog1;//CGood good2(good1);
注:默认拷贝构造函数是一个浅拷贝,重复释放的堆内存,程序崩溃
将红色部分释放,good2则变为野指针
4.赋值运算符的重载函数:把一个已存在的对象赋值给相同类型的已存在对象
实现过程:
1.判断是否自赋值
2.释放旧资源
3.申请新资源
4.赋值
CGoods& operator=(const CGoods& rhs)
{
std::cout << this << " :CGoods::operator=(const CGoods&)" << std::endl;
if (this != &rhs)//判断是否自赋值
{
delete[] mname;//释放旧资源
mname = new char[strlen(rhs.mname) + 1]();//申请新资源
strcpy(mname, rhs.mname);//赋值
mprice = rhs.mprice;
mamount = rhs.mamount;
}
return *this;
}
调用点:
CGoods good1("good1", 10.5, 20);
CGoods good2;
good2 = good1;
注:系统提供的的默认赋值运算符重载函数是 浅拷贝
const作用:
1.防止修改实参
2.接收隐式生成的临时对象
以下两种默认函数暂不讨论:
5.取地址操作符的重载函数
6.const修饰的取地址操作符的重载函数
7、临时量
1.内置类型的临时量:常量
2.自定义类型的临时量:变量
3.隐式生存的临时量:常量
4.临时对象的生存周期:表达式结束
临时量的优化
1.以生成临时对象的方式生成新对象
CGood good4(10);
此行代码,是将int类型作为形参传递生成一个CGood类型,明显不适合,所以将10生成一个带有一个参数的CGoodt类型临时对象,再调用拷贝构造函数,用已存在的对象来生成一个相同类型的对象,而临时对象的生成周期为表达式结束,所以在遇到分号“;”时,再调用析构函数释放临时对象。
2.引用能提升临时对象的生存周期:把临时对象提升和引用变量相同的生存周期
CGoods& rgood12 = CGoods("good11", 10.1, 20);
此行代码,是显式生成一个带有三个参数的CGood类型的临时对象,再调用拷贝构造函数,用已存在的对象来生成一个相同类型的对象,而临时对象的生成周期为表达式结束,但使用引用符号“&”,能提升临时对象的生存周期,把临时对象提升和引用变量相同的生存周期,所以本应该在遇到分号时,调用析构函数,但延长生存周期后,在程序结束时才会调用析构函数。
练习:分析每一行代码所存储的位置,以及调用的函数、生存周期
CGoods ggood1("good1", 10.1, 20);//.data
int main()
{
CGoods good3;
CGoods good4(good3);
good4 = good3;
static CGoods good5("good5", 10.1, 20);//.data
CGoods good6 = 10;
CGoods good7(10);
CGoods good8 = CGoods("good8", 10.1, 20);
good6 = 20;
good7 = CGoods(20);
good8 = (CGoods)("good8",10.1, 20);
CGoods* pgood9 = new CGoods("good9", 10.1, 20);//heap
CGoods* pgood10 = new CGoods[2];
CGoods* pgood11 = &CGoods("good11", 10.1, 20);
CGoods& rgood12 = CGoods("good11", 10.1, 20);
const CGoods& rgood13 = 20;
delete pgood9;
delete[] pgood10;
return 0;
}
CGoods ggood2("good2", 10.1, 20);//.data
9、构造函数的初始化列表
初始化顺序和初始化列表的顺序无关
和成员变量/对象的声明顺序有关
Test(int a,int b):ma(a),mb(b)
{}
10、常对象和常方法:
常对象不能调用普通方法
常对象只能调用常方法
普通对象也可以调用常方法
普通方法可以调用常方法
常方法不能调用普通方法
void Show()const
{}
//将this指针修饰为const Test *const类型
常成员变量:一定要初始化 (构造函数的初始化列表中)
Stu (const Stu&rhs):birth(rhs.birth);
11、static
1.修饰成员变量:不属于对象独享 、对象共享、一定要在类外初始化、不依赖对象访问
static int ma;
int Text::ma=10;//注意使用时需要加作用域
2.修饰成员方法:(_cdecl 没this指针)不能访问普通成员变量、可以访问静态的成员变量和全局变量、
静态的成员方法不能调用普通的成员方法
普通的成员方法可以调用静态的成员方法
12、类编译过程(顺序)
1.类名
2.类成员名称
3.成员方法的形参,默认值
4.方法体