正片开始
C++中的类
注意要点:- 当类中没有任何数据时,此时类所占大小为1个字节
- 在类中引入了权限限定词如:public(公有属性),protected(保护属性),private(私有属性)
- 当类中的数据没有被权限限定时,默认为私有属性
- 当类中的成员函数访问类中的数据时无权限,可以随意访问
- 在类外访问类中数据时只能访问公有属性
如何创建类,并访问类中的数据
什么是类呢?其实就是结构体换了个关键字,将struct 换成了class,很简单吧!
让我们看看用代码如何创建一个类
#include<iostream>
#include<string>
using namespace std;
class MM
{
int age = 18; //无权限限定默认为私有属性
public: //公有属性
void print_data()
{
cout << "姓名:" << name << endl << "年龄:" << age << endl << "身份证号:" << ID << endl << "地址:" << pos << endl<<"家产:"<<money<<endl;
}
string explain = "我是公有属性,可以直接访问";
protected: //保护熟悉
string ID = "5155354512542153";
string name = "小明";
string pos = "四川";
private: //私有熟悉
string money = "10000元";
};
int main()
{
MM mm;
//公有属性访问
cout << mm.explain << endl;
mm.print_data();
//下面这种是错误写法,不是公有属性无法直接访问
//mm.age;
//mm.ID;
//mm.name;
//mm.pos;
//mm.money;
return 0;
}
输出结果
我是公有属性,可以直接访问
姓名:小明
年龄:18
身份证号:5155354512542153
地址:四川
家产:10000元
C++中的构造函数
注意要点:
- 构造函数的名字必须和类名相同,且无需返回值,参数随意
- 构造函数是用来创建对象的,所以一般情况属于public(公有属性)
- 不写构造函数存在一个默认的无参构造函数,写了任何形式的构造函数,则默认的构造函数不存在
- 创建对象时,程序自己调用构造函数,如果构造函数带参,则创建对象时也必须带参
- 构造函数也满足函数缺省以及函数重载功能,在类中声明类外实现与结构体和空间命名规则一样
返回类型 类名::函数名(参数){函数体};
构造函数中的关键字default和delete
- default
class MM
{
public:
MM()=default //我自己创建了个构造函数,用default告诉程序我要使用默认的构造函数,此时程序会调用默认的构造函数,比自己写的构造函数效率快。
};
- delete
class MM
{
public:
MM()=delete;//删除默认的构造函数,此时这个类已经无法创建变量
};
int main()
{
MM mm; //此变量已无法创建
}
构造函数的实现
#include<iostream>
#include<string>
using namespace std;
class GG
{
public:
GG(int m_age,string m_name)
{
age=m_age;
name=m_name;
cout<<"构造函数已被调用"<<endl;
}
void print_data()
{
cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl;
}
protected:
int age;
string name;
};
int main()
{
//此时自己写了有参构造函数,创建变量的方式发生变化
GG gg(18,"人妖"); //创建此变量时会自动调用构造函数
gg.print_data();
return 0;
}
输出结果
构造函数已被调用
年龄:18
姓名:人妖
析构函数
如果在类中有指向(自由存储区/堆区)的指针,则需要自己写一个析构函数来释放这块空间,要不然会存在内存泄漏。
#include<iostream>
#include<string>
using namespace std;
class GG
{
public:
GG(const char* str) //构造函数
{
int len = sizeof(str); //计算字符串的个数
p_str = new char[len];
strcpy(p_str, str);
}
void print_data()
{
cout << p_str << endl;
}
~GG() //析构函数
{
delete[] p_str;
cout << "已释放完毕!" << endl;
}
protected:
char* p_str;
};
int main()
{
//程序死亡之前,系统会自己调用析构函数释放空间
GG gg("武大郎");
gg.print_data();
return 0;
}
输出结果
武大郎
已释放完毕!
构造和析构顺序问题
代码解释
#include<iostream>
#include<string>
using namespace std;
class DD
{
public:
DD(string m_str = "A")
{
str = m_str;
cout << m_str << " ";
}
~DD()
{
cout << str << " ";
}
protected:
string str;
};
int main()
{
//构造函数和析构函数调用顺序相反
{
cout << "第一组测试:" << endl;
cout << "构造:" << endl;
DD d("Z");
DD dd("X");
DD ddd("C");
cout << endl << "析构:" << endl;
};
cout << "\n\n";
{
cout << endl<< "第二组综合测试:" << endl;
cout << "构造:" << endl;
DD f("Q");
static DD ff("W");
DD* p = new DD("E");
DD fff("R");
delete p;
DD ffff("T"); //输出结果 Q W E R E T T R Q W
cout << endl << "析构:" << endl;
}
return 0;
}
注意:在没有 “静态变量” 和 " 自由存储区变量" 时 构造函数和析构函数调用顺序是相反的,如上述第一组代码测试,如果有 " 自由存储区变量" 时 遇到delete就调用析构,记住如果有静态变量,则静态变量调用的析构函数永远是最后一个
拷贝构造函数
注意:拷贝构造函数的参数必须是引用,如果是传值则会无限递归下去#include<iostream>
#include<string>
using namespace std;
class MM
{
public:
MM(string _name = "张三", int _age = 18, string _sex = "人妖")
{
name = _name;
age = _age;
sex = _sex;
}
MM(const MM& param)
{
name = param.name;
age = param.age;
sex = param.sex;
}
protected:
string name;
int age;
string sex;
};
int main()
{
MM m1;
//拷贝的第一种写法
MM m2(m1);
//拷贝的第二种写法
MM m3=m1;
return 0;
}
可以看见在创建变量m2,m3时实现了将m1的数据拷贝给m2,m3的过程,在拷贝时会自动调用拷贝函数,如果自己没有写拷贝函数,系统会有默认的拷贝函数
浅拷贝
浅拷贝就是在类中由系统自带的默认拷贝构造函数,它会将一些简单的数据拷贝到另一个对象中,如果存在,在堆区和自由存储区开辟内存,当拷贝完时,两个对象都拥有了这块地址的指向,当释放内存时,会对这块内存多次释放,造成内存多次释放问题。class MM
{
public:
MM(const char* str)
{
len = sizeof(str); //计算字符串长度
p_str = new char[len];
strcpy(p_str, str);
}
//系统默认的拷贝构造函数功能实现
MM(const MM¶m)
{
len=param.len;
p_str=param.p_str; //此时m2对象中的p_str和m1中的p_str都指向了同一块内存,释放会出现多次释放问题
}
~MM()
{
if (p_str != NULL)
{
delete[] p_str;
p_str = NULL;
}
}
protected:
char* p_str;
int len;
};
int main()
{
MM m1("张三");
MM m2(m1);
return 0;
}
深拷贝
为了解决上述的情况,引入了深拷贝,就是将堆区或者自由存储区的空间大小重新开辟一份,将内容拷贝到新开辟的空间中,让m2中的指针指向新开辟的空间,这样就不会造成对同一块内存多次释放问题,代码实现如下class MM
{
public:
MM(const char* str)
{
len = sizeof(str); //计算字符串长度
p_str = new char[len];
strcpy(p_str, str);
}
MM(const MM& param)
{
len = param.len;
p_str = new char[len];
strcpy(p_str, param.p_str);
}
~MM()
{
if (p_str != NULL)
{
delete[] p_str;
p_str = NULL;
}
}
protected:
char* p_str;
int len;
};
int main()
{
MM m1("张三");
MM m2(m1);
return 0;
}
完