1、面向对象概述:更符合人的思维,基于面向过程 2、类和对象 抽象和类 编写程序的目的:就是为了模拟现实世界的事务,解决现实中的问题,实现信息化。 定义类: 通常,将接口(类声明)放在头文件中,并将实现(类方法的代码)放在源代码文件中。 使用class关键字声明类 类定义一般有两部分: 1、类声明:成员变量、成员方法 放到头文件中 2、类方法定义:成员方法的具体实现 放到源代码文件中
3、学生类的编写与使用:
头文件声明(类声明):
#pragma once #include <iostream> #include <string> using namespace std; // 声明一个类使用class关键字,后边跟上类名{} class Student { public: //权限 string m_Name; // 姓名 int m_Age; // 年龄 int m_Id; // 学号 void sleep(); //睡觉的方法 void eat(); void study(); };
类方法定义:
#include "Student.h" #include <iostream> using namespace std; // 使用Student:: 来标识一下类作用域 void Student::sleep() { cout << "学生睡觉" << endl; } void Student::eat() { cout << "学生吃饭" << endl; } void Student::study(){ cout << "学生学习" << endl; }
使用学生类:
#include <iostream> #include "Student.h" using namespace std; // 编写一个学生类,创建学生对象,使用学生对象 int main() { // 创建学生对象 Student s1; //把Student对象创建在了栈区 // 使用学生对象 s1.m_Name = "zs"; s1.m_Age = 20; s1.m_Id = 1101; cout << s1.m_Name << "\t" << s1.m_Age << "\t" << endl; // 调用对象的方法 s1.sleep(); s1.eat(); s1.study(); // 动态创建 Student* p_stu = new Student; p_stu->sleep(); return 0; }
7、构造函数和析构函数 构造函数:主要作用就是用于在创建对象时为对象的成员进行初始化 析构函数:主要作用就是用于在对象销毁前,执行一些清理工作 构造函数和析构函数,如果没有手动提供,编译器会提供一个默认的。 默认的构造函数和析构函数中什么都不做。编译器会强制执行。 构造函数和析构函数写到公共权限下 构造函数: 1、构造函数名和类名相同 2、无返回值,也不用写void 3、允许多个参数,可以重载 析构函数: 1、析构函数名和类名相同,前面加上~ 2、没有返回值,不用写void 3、不允许有参数,不支持重载 构造函数和析构函数由编译器自动调用,无需手动调用 8、构造函数分类和调用 构造函数的分类: 分类方式1:有参构造 无参构造(默认构造) 分类方式2:普通构造 拷贝构造
拷贝构造函数示例
Person(const Person& p) { m_Age = p.m_Age; cout << "拷贝函数执行了..." << endl; }
构造函数的调用: 方式一(显式调用): Person p1 = Person(); Person p2 = Person(30); Person p5 = Person(p1); // 显式调用拷贝构造函数 方式二(隐式调用): Person p3; // 结尾不加圆括号 Person p3();是不对的 Person p4(30); Person p6(p1); // 隐式调用拷贝构造函数 匿名对象: Person(); 本行代码执行完之后立即释放 隐式转换法: Person p7 = 10; // 不报错,因为编译器会转换成Person p7 = Person(10); 利用隐式转换法调用拷贝构造函数 Person p8 = p7; // 编译器作转换:Person p8 = Person(p7); 初始化列表 Person p8 = {}; // 调用无参构造 Person p9 {20}; // 调用有参构造 相当于Person p9 = {20}; 9、拷贝构造函数和调用时机 Person(const Person& p){ // 加长const表示不让人对这个引入的Person进行修改 m_Age = p.m_Age; // 传递引用节省内存 cout << " 拷贝构造函数 " << endl; } 调用时机: (1)用一个对象初始化另一个对象 (2)对象以值传递的方式 传递给函数参数 void doWork(Person p){ } (3)函数局部对象以值传递的方式从函数返回 Person doWork1(){ Person p(30); return p; } 返回值的优化:编译器会采用一种优化RVO( Return Value Optimization )。 10、构造函数的调用规则 * 默认情况下,c++编译器至少会为我们提供类的3个函数 (1)默认构造函数,无参,函数体为空 (2)默认析构函数,无参,函数体为空 (3)默认的拷贝构造函数,对类中非静态成员属性进行简单的值拷贝 11、深拷贝和浅拷贝 Person(char* name, int age) { // m_Name = name; 不可取 m_name = new char(strlen(name) + 1); strcpy(m_Name, name); m_Age = age; } // 在构造函数里使用new关键字在堆里开辟了空间,那么在对象被销毁的时候,堆里的空间是没有被释放的,因为没有地方执行delete语句,那么我们需要在析构函数中释放那部分空间: ~Person() { if( m_name != nullptr ) { delete[] m_Name; m_Name = nullptr; } } 浅拷贝:就是简单的值传递,不会重新为指针变量开辟内存 12、初始化列表 一种语法,用来对成员数据进行初始化 一般用来对const修饰的成员进行初始化,当然其他成员也可以。 对于引用类型的成员,也必须使用初始化列表的语法进行初始化 语法示例: Person() : m_A(10), m_B(20), m_C(30) { // 构造函数 } // 也可以这样 Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c) { // 构造函数 } 13、类对象作为类成员 当其他类对象作为本类中的成员,构造的顺序是:先调用其他类的构造,再调用本类的构造。析构的顺序与之相反。 成员的构造先后顺序是根据在类中的先后顺序决定的,跟构造方法中的初始化顺序无关。 14、静态成员