类和对象
- C++面向对象的三大特征:
– 封装 – 继承 – 多态 - C++认为万事万物都皆为对象,对象上有其属性和行为
- 具有相同性质的对象,我们可以抽象称为类
封装
封装的意义
- 封装是C++面向对象三大特征之一
- 封装的意义:
– 1、将属性和行为作为一个整体,表现生活中的事物 – 2、将属性和行为加以权限控制 封装意义1 - 语法:class 类名 { 访问权限: 属性 / 行为 };
- 例:设计一个圆类,求圆的周长
#include <iostream>
using namespace std;
const double PI = 3.14;
class Circle
{
public:
int m_r;
double calculataZC()
{
return 2 * PI * m_r;
}
};
int main() {
Circle c1;
c1.m_r = 10;
cout << "圆的周长为:" << c1.calculataZC() << endl;
system("pause");
return 0;
}
- 类中的属性和行为统称为成员
- 属性又叫做成员属性或成员变量
- 行为又叫做成员函数或成员方法
封装意义2
- 类在设计时,可以把属性和行为放在不同的权限下,加以控制
- 三种访问权限:
– 1、公共权限 public 成员 类内可以访问,类外可以访问 – 2、保护权限 protected 成员 类内可以访问,类外不可以访问 – 3、私有权限 private 成员 类内可以访问,类外不可以访问
#include <iostream>
using namespace std;
class Person
{
public:
string m_Name;
protected:
string m_Car;
private:
int m_Password;
public:
void func()
{
m_Name = "张三";
m_Car = "拖拉机";
m_Password = 12345;
}
};
int main() {
Person p1;
p1.m_Name = "李四";
system("pause");
return 0;
}
struct和class的区别
在C++中struct和class唯一的区别就在于默认访问权限不同:
- struct默认权限为公共
- class默认权限为私有
#include <iostream>
using namespace std;
class C1
{
int m_A;
};
struct C2
{
int m_A;
};
int main() {
C1 c1;
C2 c2;
c2.m_A = 100;
system("pause");
return 0;
}
成员属性设置为私有
优点:
- 1、将所有成员属性设置为私有,可以自己控制读写权限
- 2、对于写权限,我们可以检测数据的有效性
#include <iostream>
using namespace std;
#include <string>
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)
{
m_Age = 0;
cout << "您输入的年龄有误" << endl;
return;
}
else
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(1000);
cout << "年龄为: " << p.getAge() << endl;
p.setAge(18);
cout << "年龄为: " << p.getAge() << endl;
p.setLover("苍井");
system("pause");
return 0;
}
对象的初始化和清理
- C++中的面向对象来源于生活,每个对象都有初始设置以及对象销毁前的清除数据的设置
构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题:
- 一个对象或者变量没有初始状态,对其使用后果是未知
- 同样的使用完一个对象或变量,没有及时清理,也会造成移动的安全问题
C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工具 - 对象的初始化和清理工作是编译器强制要我们做的事情,因此我们不提供构造和析构,编译器会提供
- 编译器提供的构造函数和析构函数是空实现
构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
构造函数语法:类名(){}
- 1、构造函数,没有返回值也不写void
- 2、函数名称与类名相同
- 3、构造函数可以有参数,因此可以发生重载
- 4、程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次
析构函数:主要作用于对象销毁前系统自动调用,执行一些清理工作
析构函数语法:~类名(){}
- 1、析构函数,没有返回值也不写void
- 2、函数名称与类名相同,在名称前加上符号~
- 3、析构函数不可以有参数,因此不可以发生重载
- 4、程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
Person()
{
cout << "Person 构造函数的调用" << endl;
}
~Person()
{
cout << "Person 析构函数的调用" << endl;
}
};
void test01()
{
Person p;
}
int main() {
test01();
Person p;
system("pause");
return 0;
}
构造函数的分类及调用
两种分类方式:
- 按参数分为:由参构造和无参构造
- 按类型分为:普通构造和拷贝构造
三种调用方法:
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的无参构造函数调用" << endl;
}
Person(int a)
{
age = a;
cout << "Person 的有参构造函数调用" << endl;
}
Person(const Person &p)
{
age = p.age;
cout << "Person 的拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 的析构函数调用" << endl;
}
int age;
};
void test01()
{
Person p1;
Person p2(10);
Person p3(p2);
cout << "p2的年龄为:" << p2.age << endl;
cout << "p3的年龄为:" << p3.age << endl;
Person p4 = Person(10);
Person p5 = Person(p2);
Person p6 = 10;
Person p7 = p4;
}
int main() {
test01();
system("pause");
return 0;
}
拷贝构造函数调用时机
C++种拷贝构造函数调用时机通常由三种情况:
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的无参构造函数调用" << endl;
}
Person(int age)
{
m_age = age;
cout << "Person 的有参构造函数调用" << endl;
}
Person(const Person &p)
{
m_age = p.m_age;
cout << "Person 的拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 的析构函数调用" << endl;
}
int m_age;
};
void test01()
{
Person p1(20);
Person p2(p1);
cout << "p2的年龄为:" << p2.m_age << endl;
}
void dowork(Person p)
{
}
void test02()
{
Person p;
dowork(p);
}
Person dowork2()
{
Person p1;
return p1;
}
void test03()
{
Person p = dowork2();
}
int main() {
test01();
test02();
test03();
system("pause");
return 0;
}
构造函数调用规则
默认情况下,C++编译器至少给一个类添加3个函数
- 1、默认构造函数(无参,函数体为空)(空实现)
- 2、默认析构函数(无参,函数体为空)(空实现)
- 3、默认拷贝构造函数,对属性进行值拷贝 (值拷贝)
构造函数调用规则如下:
- 1、如果用户定义由参构造函数,C++不再提供无参构造,但是会提供默认拷贝构造
- 2、如果用户定义拷贝构造函数,C++不会再提供其他构造函数
规则1
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的默认构造函数调用" << endl;
}
Person(int age)
{
m_age = age;
cout << "Person 的有参构造函数调用" << endl;
}
~Person()
{
cout << "Person 的析构函数调用" << endl;
}
int m_age;
};
void test01()
{
Person p;
p.m_age = 18;
Person p2(p);
cout << "p2的年龄为:" << p2.m_age << endl;
}
void test02()
{
Person p1(18);
Person p3(p1);
}
int main() {
test01();
test02();
system("pause");
return 0;
}
规则2:
#include <iostream>
using namespace std;
class Person
{
public:
Person(const Person &p)
{
m_age = p.m_age;
cout << "Person 的拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 的析构函数调用" << endl;
}
int m_age;
};
void test02()
{
}
int main() {
test02();
system("pause");
return 0;
}
深拷贝和浅拷贝
- 浅拷贝:简单的赋值拷贝操作
- 深拷贝:在堆区重新申请空间,进行拷贝操作
错误示范:
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的默认构造函数调用" << endl;
}
Person(int age,int height)
{
m_age = age;
m_height = new int(height);
cout << "Person 的有参构造函数调用" << endl;
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "Person 的析构函数调用" << endl;
}
int m_age;
int *m_height;
};
void test01()
{
Person p1(18,160);
cout << "p1的年龄为:" << p1.m_age << "身高为:" << *p1.m_height << endl;
Person p2(p1);
cout << "p2的年龄为:" << p2.m_age << "身高为:" << *p2.m_height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
错误原因: 两次释放同一堆区内存空间,第二次为非法操作
利用深拷贝修改
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的默认构造函数调用" << endl;
}
Person(int age,int height)
{
m_age = age;
m_height = new int(height);
cout << "Person 的有参构造函数调用" << endl;
}
Person(const Person &p)
{
cout << "Person 的拷贝构造函数调用" << endl;
m_age = p.m_age;
m_height = new int(*p.m_height);
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "Person 的析构函数调用" << endl;
}
int m_age;
int *m_height;
};
void test01()
{
Person p1(18,160);
cout << "p1的年龄为:" << p1.m_age << "身高为:" << *p1.m_height << endl;
Person p2(p1);
cout << "p2的年龄为:" << p2.m_age << "身高为:" << *p2.m_height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
初始化列表
- 作用:C++提供了初始化列表语法,用来初始化属性
- 语法:构造函数():属性1(值1),属性2(值2)…{}
#include <iostream>
using namespace std;
class Person
{
public:
Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c)
{
}
int m_A;
int m_B;
int m_C;
};
void test01()
{
Person p(30,20,10);
cout << "m_A = " << p.m_A << " m_B = " << p.m_B << " m_C = " << p.m_C << endl;
}
int main() {
test01();
system("pause");
return 0;
}
注意:冒号位置 类对象作为类成员
- C++类中的成员可以时另一个类的对象,我们称该成员为对象成员
#include <iostream>
using namespace std;
#include <string>
class Phone
{
public:
Phone(string pname)
{
m_Pname = pname;
cout << "Phone的构造函数调用" << endl;
}
~Phone()
{
cout << "Phone的析构函数调用" << endl;
}
string m_Pname;
};
class Person
{
public:
Person(string name, string pname) :m_name(name), m_phone(pname)
{
cout << "Person的构造函数调用" << endl;
}
~Person()
{
cout << "Person的析构函数调用" << endl;
}
string m_name;
Phone m_phone;
};
void test01()
{
Person p("张三", "苹果MAX");
cout << p.m_name << "拿着:" << p.m_phone.m_Pname << endl;
}
int main() {
test01();
system("pause");
return 0;
}
当其他类对象作为本类成员,构造时先构造类对象,再构造自身 当其他类对象作为本类成员,析构时先析构自身,再析构对象
静态成员 静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分类:
- 静态成员变量:
– 所有对象共享同一份数据 – 在编译阶段分配内存 – 类内声明,类外初始化 - 静态成员函数:
– 所有对象共享同一个函数 – 静态成员函数只能访问静态成员变量
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
static void func()
{
m_a = 100;
cout << "static void func的调用" << endl;
}
static int m_a;
int m_b;
private:
static void func2()
{
cout << "static void func2的调用" << endl;
}
};
int Person::m_a = 0;
void test01()
{
Person p;
p.func();
Person::func();
}
int main() {
test01();
system("pause");
return 0;
}
C++对象模型和this指针
成员变量和成员函数分开储存
- 在C++中,类内的成员变量和成员函数分开储存
- 只有非静态成员变量才属于类的对象上
空对象占用内存空间为1
#include <iostream>
using namespace std;
#include <string>
class Person
{
};
void test01()
{
Person p;
cout << "size of p=" << sizeof(p) << endl;
}
int main() {
test01();
system("pause");
return 0;
}
成员变量和成员函数分开储存
#include <iostream>
using namespace std;
#include <string>
class Person
{
int m_a;
static int m_b;
void func()
{ }
static void func2() {}
};
int Person::m_b = 0;
void test01()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
}
int main() {
test01();
system("pause");
return 0;
}
this指针概念
- this指针指向被调用的成员函数所属的对象
- this指针是隐含每一个非静态成员函数内的一种指针
- this指针不需要定义,直接使用即可
- this指针的用途:
– 当形参和成员变量同名时,可用this指针来区分 – 在类的非静态成员函数中返回对象本身,可使用return * this
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
Person(int age)
{
this->age = age;
}
Person & PersonAddage(Person &p)
{
this->age += p.age;
return *this;
}
int age;
};
void test01()
{
Person p1(18);
cout << "p1的年龄为:" << p1.age << endl;
}
void test02()
{
Person p1(10);
Person p2(10);
p2.PersonAddage(p1).PersonAddage(p1).PersonAddage(p1);
cout << "p2的年龄为:" << p2.age << endl;
}
int main() {
test01();
test02();
system("pause");
return 0;
}
空指针访问成员函数
- C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
- 如果用到this指针,需要加以判断保证代码的健壮性
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
void showclassname()
{
cout << "this is Person class" << endl;
}
void shoePersonage()
{
if (this == NULL)
{
return;
}
cout << "age = " << this->m_age << endl;
}
int m_age;
};
void test01()
{
Person *p = NULL;
p->showclassname();
}
int main() {
test01();
system("pause");
return 0;
}
const修饰成员函数
常函数
- 成员函数后加const后我们称这个函数为常数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
void showPeson() const
{
this->m_b = 100;
}
void func()
{
m_a = 100;
}
int m_a;
mutable int m_b;
};
void test01()
{
Person p;
p.showPeson();
}
void test02()
{
const Person p;
p.m_b = 100;
p.showPeson();
}
int main() {
test01();
system("pause");
return 0;
}
|