面向对象(C++语言核心内容)
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、静态成员
* 在类定义种,它的成员(成员变量,成员方法),这些成员都可以用关键字static声明,称之为静态成员。
* 特点:不管这个类创建了多少个对象,静态对象只有一个拷贝。这个拷贝被所有属于这个类的对象所共享。
* 静态成员变量:
1、必须在类中声明,在类外定义。
eg:
class Person {
public:
int m_A; // 普通的成员变量
int m_B; // 静态的成员变量,在类内声明
};
int Person::m_B = 100; // 在类外定义(初始化)
15、单例设计模式
单例:单个实例。
设计模式:程序员的经验总结
单例设计模式:一个类在一个程序中只能有一个对象。
单例的好处:防止创建过多的对象。节省内存。
具体实现:
1)所有的构造函数都要私有化
2)创建一个静态的实例,要私有化
16、this指针
this指针本质上是个指针常量
17、常函数和常对象
* 常函数
void show() const{ //在这里加上const,那么在这个函数里,this指针无法修改其指向的内容了。
this.age = 200; //报错,因为常函数没法操作this指向的内容
}
* 用const修饰成员函数时,const修饰this指针指向的内存区域。成员函数体内,不能修改本类中的任何普通成员变量。这个函数我们称之为常函数。
* 例外:当成员变量类型前面被mutable修饰除外。
eg:mutable int height;
*常对象
创建对象时:
const Person p2(30);
常对象可以修改mutable修饰的成员变量
常对象只能调用常函数,不能调用普通函数。
18、友元成员函数(全局)
类的一个特点是数据隐藏(private)
友元有三种:
1、友元(全局)函数
(1)在类中声明,函数前加关键字friend
(2)在全局区域内定义
2、友元类
3、友元成员函数
19、友元类:在类外访问一个类的私有成员。
20、友元成员函数(局部)
友元的注意事项:
(1)、友元关系不能被继承
(2)、友元关系是单向的,类A是类B的朋友,类B未必是类A的朋友
(3)、友元关系不具有传递性