- 对象的初始化和清理:
- 对象的初始化和清理是两个非常重要的安全问题:
- 一个对象或者变量没有初始状态,对其使用的后果是未知的
- 一个对象或者变量使用完,没有及时清理,也会造成一定的安全问题
- 对象的初始化和清理是两个非常重要的安全问题:
C++利用了构造函数和析构函数解决上述问题,这两个函数会被编译器自动调用,完成对象初始化和清理工作,对象初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造函数和析构函数,编译器会提供,编译器提供的构造函数与析构函数是空实现。
- 构造函数:为对象的成员属性赋值,系统自动调用
- 语法: 类名(){ }
- 构造函数,没有返回值也不写 void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时会自动调用,无需手动且只调用一次
- 语法: 类名(){ }
- 析构函数:用于对象销毁前系统自动调用,执行一些清理工作
- 语法:~类名(){ }
- 析构函数,没有返回值也不写 void
- 函数名称与类名相同,在类名前加上 ~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用,无需手动且只调用一次
- 语法:~类名(){ }
#include <iostream>
using namespace std;
//对象初始化与清理
class Person
{
public:
//构造函数
Person()
{
cout << "hello" << endl;
}
//析构函数
~Person()
{
cout << "world" << endl;
}
};
int main()
{
Person p;
system("pause");
return 0;
}
- 构造函数的分类及调用
- 两种分类方式
- 按参数分:有参构造和无参构造(默认构造)
- 按类型分:普通构造和拷贝构造
-
//拷贝构造函数 Person(const Person &p) { //将传入的所有属性拷贝到我身上 }
-
- 三种调用方式
- 括号法
- 显示法
- 隐式转换法
-
class Person { public: //无参构造函数 Person() { cout << "无参构造" << endl; } //有参构造 Person(int a) { cout << "有参构造" << endl; } //拷贝构造函数 Person(const Person &p) { cout << "拷贝构造" << endl; } //析构函数 ~Person() { cout << "析构函数" << endl; } }; void test01() { //括号法 Person p1; //调用默认构造函数 Person p2(10); //调用有参构造函数 Person p3(p2); //调用拷贝构造函数 //注意事项:调用默认构造函数不要加() cout << endl; //显示法 Person p4; Person p5 = Person(10); //调用有参构造 Person p6 = Person(p5); //调用拷贝构造 //Person(10): 匿名对象,当前执行结束,系统会立即回收匿名对象 //不要利用拷贝构造函数,初始化匿名对象 //隐式转换法 Person p7 = 10; //相当于Person p7 = Person(10); Person p8 = p7; //相当于Person p8 = Person(p7); }
-
- 两种分类方式
- 拷贝构造函数调用时机
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
-
class Person { public: Person() { cout << "默认构造" << endl; } Person(int age) { cout << "有参构造" << endl; m_Age = age; } Person(const Person &p) { cout << "拷贝构造" << endl; m_Age = p.m_Age; } ~Person() { cout << "析构函数" << endl; } int m_Age; }; //使用一个已经创建完毕的对象来初始化一个新对象 void test01() { Person p1(20); Person p2(p1); cout << "p2的年龄" << "\t" << p2.m_Age << endl; } //值传递的方式给函数参数传值 void doWork1(Person p) { } void test02() { Person p; doWork1(p); } //以值方式返回局部对象 Person doWork2() { Person p1; return p1; } void test03() { Person p = doWork2(); }
-
- 构造函数调用规则
- 创建一个类,c++默认提供3个函数:默认构造(空实现)、拷贝构造(值拷贝)、析构函数(空实现)
- 如果用户定义有参构造函数,c++不再提供默认无参构造函数,但是会提供默认拷贝构造函数
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
- 深拷贝与浅拷贝
- 浅拷贝:简单的赋值拷贝操作(会造成堆区内存重复释放)
- 深拷贝:在堆区重新申请空间,进行拷贝操作(如果属性有在堆区开辟的,一定要自己提供拷贝构造函数)
-
Person(const Person& p) { m_Age = p.m_Age; m_Name = p.m_Name; //深拷贝 m_Height = new int(*p.m_Height); } ~Person() { //将堆区开辟的数据做释放操作 if (m_Height != NULL) { delete m_Height; m_Height = NULL; } cout << "析构函数" << endl; } int m_Age; string m_Name; int* m_Height;
-
- 初始化列表
- 作用:c++提供了初始化列表语法,用来初始化属性
- 语法:构造函数():属性1(值1),属性2(值2)..........{}
-
.class num { public: //传统初始化 //num(int a, int b, int c) //{ // m_A = a; // m_B = b; // m_C = c; //} //初始化列表 num(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {} int m_A; int m_B; int m_C; }; int main() { num(30, 40, 50); return 0; }
-
- 类对象作为类成员
- c++类中的成员可以是另一个类的对象,我们称该成员为对象成员
- 构造时先构造类对象,再构造自身,析构顺序与构造顺序相反
- 静态成员
- 静态成员变量
- 所有对象共享一份数据
- 再编译阶段分配内存
- 类内声明,类外初始化
- 两种访问方式:
- 通过对象进行访问(对象 . )
- 通过类名进行访问(类名::变量)
-
#include <iostream> using namespace std; class Person { public: //静态成员变量 static int m_A; //类内声明 }; int Person::m_A = 100; //类外初始化 int main() { Person p1; Person p2; p2.m_A = 200; cout << p1.m_A << endl; return 0; }
-
- 静态成员函数
- 所有对象共享一个函数
- 静态成员函数只能访问静态成员变量
-
#include <iostream> using namespace std; class Person { public: //静态成员变量 static int m_A; //类内声明 static void func() { m_A = 200; } }; int Person::m_A = 100; //类外初始化 int main() { Person p1; Person::func(); cout << p1.m_A << endl; return 0; }
-
- 静态成员变量
- c++对象模型和this指针
- 在c++中,类内的成员变量和成员函数分开存储,只有非静态成员才属于类的对象上
- this指针指向被调用的成员函数所属的对象
- 用途:
- 当形参和成员变量同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this
-
#include <iostream> using namespace std; class Person { public: Person(int age) { this->age = age; } int age; Person& test(Person& p) { this->age += p.age; return *this; } }; int main() { Person p1(18); //链式编程思想 p1.test(p1).test(p1).test(p1); cout << p1.age << endl; return 0; }
-
- 用途:
- 空指针访问成员函数
- c++中空指针也是可以调用成员函数的,但也要注意有没有用到this指针,如果用到this指针,需要加以判断保证代码的健壮性
-
class Person { public: void showClassName() { cout << "this is Person class" << endl; } void showPersonAge() { if (this == NULL) { return; } cout << "Age=" << m_Age << endl; } int m_Age=10; }; void test01() { Person* p = NULL; p->showClassName(); //空指针访问里面的属性,访问不到 p->showPersonAge(); }
- const修饰成员函数
- 常函数:
- 成员函数后加const后我们称这个函数为常函数
- 常函数内不可修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
- 常对象
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
-
class Person { public: void showPerson() const //在成员函数后加const,修饰的是this指向,让this指针的值也不可以改变 { //m_A = 100; //不可修改 m_B = 100; } void show() { m_A = 100; } int m_A; mutable int m_B; }; void test() { const Person p; p.showPerson(); //p.show(); //不可调用普通成员函数 }
- 常函数: