4、类和对象
1)private、public
除了s1.setName(),还可以通过行为给属性赋值。
类中的属性和方法叫成员。
2)成员属性设置为私有
也就是通过public对private的数据可读可写
class Person {
public:
//姓名设置可读可写
void setName(string name)
{
m_Name = name;
}
string getName()
{
return m_Name;
}
//获取年龄
int getAge()
{
return m_Age;
}
//设置年龄
void setAge(int age)
{
if (age < 0 || age > 150) {
cout << "你个老妖精!" << endl;
return;
}
m_Age = age;
}
//情人设置为只写
void setLover(string lover)
{
m_Lover = lover;
}
private:
string m_Name; //可读可写 姓名
int m_Age; //只读 年龄
string m_Lover; //只写 情人
};
int main() {
Person p;
//姓名设置
p.setName("张三");
cout << "姓名: " << p.getName() << endl;
//年龄设置
p.setAge(50);
cout << "年龄: " << p.getAge() << endl;
//情人设置
p.setLover("苍井");
//cout << "情人: " << p.m_Lover << endl; //只写属性,不可以读取
system("pause");
return 0;
}
写一个类思考问题:
数据有哪些
这些数据做什么
数据放在private,行为放在public
通过public为private赋值,并操作。
2)构造和析构
生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据
保证安全
C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。
构造函数语法: 类名(){}
- 构造函数,没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法: ~类名(){} - 析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符号 ~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
class Person
{
public:
//构造函数
Person()
{
cout << "Person的构造函数调用" << endl;
}
//析构函数
~Person()
{
cout << "Person的析构函数调用" << endl;
}
};
void test01()
{
Person p; //这儿是创建对象,不是调用函数,但是还是会自动调用构造函数
}
int main()
{
test01();
system("pause");
return 0;
}
void test01()
{
Person p; //这儿是创建对象,不是调用函数,但是还是会自动调用构造函数
}
也就是创建对象时,编译器自动调用构造函数
int main()
{
//test01();
Person p; //只有构造,没有析构,继续执行到 system("pause");了,看不见析构,但是有!test是等test函数完成才析构,那是可以的
system("pause");
return 0;
}
3)构造函数分类
两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
解释:
拷贝构造函数,先传进来一个人Person p,假如这个叫张三,把身高体重给他。现在李四要拷贝它,那么张三要不变,所以要加const Person p,且用引用方式传递。
/
/有参构造函数
Person(int a) {
age = a;
cout << "有参构造函数!" << endl;
} /
/拷贝构造函数
Person(const Person& p) {
age = p.age;
cout << "拷贝构造函数!" << endl;
}
构造函数就可以将传进来的a赋给age了,且拷贝函数用age = p.age;表示
调用:
//注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
Person p; //调用无参构造函数
//2.1 括号法,常用
Person p1(10);
// 拷贝
Person p2(p1);
25 拷贝函数怎么调用
C++中拷贝构造函数调用时机通常有三种情况
使用一个已经创建完毕的对象来初始化一个新对象
值传递的方式给函数参数传值
以值方式返回局部对象
值传递只会影响临时副本,不会影响原来的
构造函数调用规则
![!](https://img-blog.csdnimg.cn/direct/3cc60e0161a74f43b8bc8e2515ebfc88.png)](https://img-
没写拷贝函数,也会进行值拷贝。
//拷贝构造函数
Person(const Person& p) {
age = p.age;
cout << "拷贝构造函数!" << endl;
}
void test01()
{
Person p1(18);
//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
Person p2(p1);
cout << "p2的年龄为: " << p2.age << endl;
}
即提供有参构,编译器不提供无参构。但提供下边的拷贝构造。(默认的)
提供拷贝,有无参构造都不提供
27 深拷贝和浅拷贝
int *mHeight = new int(height) ,是存在栈区,栈区先进后厨
浅拷贝是完全复制,而p2先释放,因此p1再去释放,就没了。产生重复释放,解决办法是深拷贝,在堆区重新申请空间。
28 初始化列表
上面写死了,下面来
传递逻辑
29 类对象作为类成员
解释如下:
类中对象有Phone m_Phone;那么它是先构造Person还是Phone呢?
/当类中成员是其他类对象时,我们称该成员为 对象成员
//构造的顺序是 :先调用对象成员的构造,再调用本类构造
//析构顺序与构造相反
人现有胳膊腿,才有完成的人,phone就相当于胳膊腿
30&31
32 成员变量和成员函数分开存储
为1,因为要为对象创建一个空间
int占4个,静态变量、非静态函数不属于成员变量,不占字节。即只有非静态属于
33 this指针
有很多对象,怎么区分是p1,还是p2,p3调用这个成员函数呢
作用:
1、解决名字冲突
2、返回对象本身用this
并不是18,因为编译器认为person类中三个age是同一份,与成员属性age不是一回事。解决办法
1)成员变量改个名
int m_age
2)this
构造函数内的
this->age = age
this指针指向被调用的成员函数所属的对象
被调用的成员函数所属的对象:是p1(对象时p1)
返回对象用this
不用return *this;p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);会错
用了每次都是返回p2,因此是可以的继续用
值返回就是20,因为会是拎一个对象了。拷贝函数
34 空指针
第一个正确,第二个错误,其实age前面默认加了个this,即this->m_age,表当前对象属性
但是当前对象为空,你还去访问它,所以错。
加个判断
35 const修饰成员函数
常函数
this是指向p的,本质是指针常量,int *const p格式,/ p(this)是一个指针常量,它的值(地址)是固定的,不能被改变。
this = null是错,this->m_a=100是对
函数后面加个const,那么就相当于const Person *const this;,指针指向的值都不可以修改了。
this->ma=100;=null都是错的
加上mutable就可以修改了
常对象
在对象前加上const
func为正常函数,不能调用,常对象只能调用常函数