c++面向对象
面向对象
- 面向对象
- 创建类
- 访问权限
- struct和class区别
- 对象的初始化和清理
- 构造函数
- 构造函数初始化列表
- C++构造函数分类
- 析构函数
- 使用new和不适用new创建对象的区别
- 方法实现在类外
- 单文件
- 最佳实践(分文件)
- 静态
- 静态属性
- this
- 常函数
- 常对象
- mutable
- 友元
- 全局函数做友元
- 成员函数做友元
- 类做友元
- 运算符重载
- 二元类内、类外重载
- ++
- 对象格式化
- 继承
- 基本语法
- 继承的特点
- 三种继承方式
- 继承中构造和析构
- 多态
- 多态时,父类对象访问子类(虚函数)
- case
- 接口
- 纯虚函数与多继承
- 虚析构函数
- 模版
- 函数模版
- 函数模版使用
- 函数模版默认值
- 当有虚拟返回值类型,可以放在第一位简化代码调用方式
- 类模版
- 使用
- 类模版继承
- 类外实现
- 类模版分文件编写
面向对象
创建类
#include <iostream>
using namespace std;
class Circle {
public:
int radius;
[[nodiscard]] double getAround() const {
return 2*3.14*radius;
}
};
int main() {
Circle c;
c.radius = 10;
double around = c.getAround();
cout << around << endl;
return 0;
}
#include <iostream>
using namespace std;
class Student {
public:
string name;
int id;
void toString() {
cout << "Name: " << name <<", id: "<<id<< endl;
}
};
int main() {
Student s1 = {"张三",3};
Student s2 = {"李四",4};
s1.toString();
s2.toString();
return 0;
}
访问权限
权限 | 说明 |
---|---|
public | 类内可以访问,类外可以访问 ,子类可以访问 |
protected | 类内可以访问,类外不可以访问,子类可以访问 |
private | 类内可以访问,类外不可以访问,子类不可访问 |
struct和class区别
在C++中struct和class唯一区别在于默认的访问权限不同
区别:
- struct默认权限是公共
- class默认权限是私有
对象的初始化和清理
构造函数和析构函数
如果不写,默认空实现
构造和析构都必须是public
构造函数
类名(){}
explicit
:修饰构造函数,不允许隐式调用
构造函数初始化列表
C++构造函数分类
参数:有参和无参
类型:普通构造和拷贝构造
拷贝构造
#include <iostream>
using namespace std;
class Person {
public:
string name;
};
int main() {
Person p1 = { "john"};
// 系统自动调用拷贝构造
Person p2 = p1;
p1.name = "33";
cout << p1.name << endl;
cout << p2.name << endl;
return 0;
}
#include <iostream>
using namespace std;
class Person {
public:
string name;
Person(string name) : name(name) {
}
// 自实现拷贝构造
Person(const Person &other)
: name(other.name) {
}
};
int main() {
Person p1 = {"john"};
// 系统自动调用拷贝构造
Person p2 = p1;
p1.name = "33";
cout << p1.name << endl;
cout << p2.name << endl;
return 0;
}
析构函数
~类名(){}
#include <iostream>
using namespace std;
class Person {
string name;
int age;
public:
Person(const string &name, const int age) {
cout << "初始化 " << endl;
this->name = name;
this->age = age;
}
~Person() {
cout << "销毁 " << name << endl;
};
};
int main() {
Person john = Person("John", 10);
return 0;
}
使用new和不适用new创建对象的区别
使用new | 没有使用new | |
---|---|---|
内存方面 | 堆开辟 | 栈开辟 |
内存管理 | 手动delete | 不需要手动销毁 |
属性初始化 | 自动默认初始值 | 没有初始值 |
语法 | 需要用 类* 来接收变量 | 不需要使用* |
成员访问 | 通过.访问 | 通过->访问 |
最佳实践:
如果说项目很大,使用new
如果是小项目,不new也可以
方法实现在类外
单文件
#include <iostream>
using namespace std;
class Person {
public:
void sleep();
};
void Person::sleep() {
cout << "I'm sleeping..." << endl;
}
int main() {
Person p;
p.sleep();
return 0;
}
最佳实践(分文件)
静态
静态属性
开辟在全局区
类内定义,类外初始化
#include <iostream>
using namespace std;
class Person {
public:
// 在类中定义的静态成员,必须在类内定义,类外进行初始化赋值
static string num;
// 如果是静态常亮,且数据类型是整型(int,short,long,long long ,char, bool),允许在定义的时候就初始化赋值
const static double PI;
static void hello() {
cout << "Hello World!" << endl;
}
};
string Person::num = "3";
const double Person::PI = 3.141592653589793;
int main() {
cout << Person::num << endl;
cout << Person::PI << endl;
Person::hello();
return 0;
}
this
#include <iostream>
using namespace std;
class MyNum {
public:
int num;
MyNum &add(int num) {
this->num = this->num + num;
return *this;
}
};
int main() {
MyNum a;
a.num = 10;
a.add(10).add(20);
cout << a.num << endl;
return 0;
}
常函数
const:可以修饰类中的成员函数,表示这个函数是一个常函数
- 使用关键字const修饰
- 常函数中,不允许修改属性值
- 常函数中,不允许调用普通函数,只能调用其他常函数
常对象
- 创建对象的时候使用关键在const修饰的对象,就是常对象
- 常对象可以读取任意属性值,但是不允许修改
- 常对象,只能调用常函数,不能调用普通函数
mutable
- 用来修饰成员属性,表示可变
- 被mutable修饰的属性,可以在常函数中修改,也可以由常对象修改
友元
类外访问类内私有成员
全局函数做友元
#include <iostream>
using namespace std;
class Home {
friend void gotoBedRoom(Home &home);// 全局函数做友元
public:
string livingRoom = "客厅";
private:
string bedRoom = "卧室";
};
void gotoBedRoom(Home &home) {
cout << home.livingRoom << endl;
cout << home.bedRoom << endl;
}
int main() {
return 0;
}
成员函数做友元
类做友元
一个类内所有的成员函数都可以访问另一个类的私有成员
运算符重载
对已有运算符重新进行定义
二元类内、类外重载
#include <iostream>
using namespace std;
class Point {
public:
int x;
int y;
Point(int x, int y): x(x), y(y) {
}
// 二元类内重载
Point operator-(const Point &other) {
return Point(x - other.x, y - other.y);
}
};
// 二元类外重载
Point operator+(const Point &a, const Point &b) {
return Point(a.x + b.x, a.y + b.y);
}
int main() {
Point p1(10, 20);
Point p2(30, 40);
Point p3 = p1 + p2;
cout << p3.x << " " << p3.y << endl;
Point p4 = p1 - p2;
cout << p4.x << " " << p4.y << endl;
return 0;
}
++
#include <iostream>
using namespace std;
class Point {
public:
int x;
int y;
Point(int x, int y): x(x), y(y) {
}
};
// 这里是运算符前置,
Point operator++(Point &p) {
p.x++;
p.y++;
return p;
}
// 运算符后置,后加加
Point operator++(Point &p, int) {
p.x++;
p.y++;
return p;
}
int main() {
Point p1(10, 20);
++p1;
p1++;
cout << p1.x << endl;
cout << p1.y << endl;
Point p2(30, 40);
return 0;
}
对象格式化
#include <iostream>
using namespace std;
class Point {
public:
int x;
int y;
Point(int x, int y): x(x), y(y) {
}
};
ostream& operator<<(ostream& os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
int main() {
Point p1(10, 20);
cout << p1 << endl;
return 0;
}
继承
基本语法
#include <iostream>
using namespace std;
class Animal {
public:
int age;
void walk() {
cout << "Walk" << endl;
}
};
class Dog : public Animal {
};
int main() {
Dog dog;
dog.walk();
return 0;
}
继承的特点
- 父类中所有的非静态成员,都可以继承给子类(排除构造函数和析构函数)
- 一个类可以被多个类同时继承
- 在C++中,一个类可以有多个类(支持多继承)
三种继承方式
- public:保留父类原有权限
- protected:超过protected权限部分,降为protected权限
- private:访问权限都为private
c++中默认使用私有继承
继承中构造和析构
继承中的构造和析构函数
子类对象在创建的时候,必须先调用父类构造,在调用子类构造。默认调用的是父类无参构造
子类对象在销毁的时候,先调用自己的析构函数,在调用父类的析构函数
Q:如果父类中没有无参构造函数,或者父类中的无参构造函数是私有的,会不会对子类产生影响?
A:会!因为子类对象在创建的时候,需要默认先调用父类中的无参构造函数
Q:如何解决这个问题?
A:给父类添加无参构造或者在子类构造函数中显示调用父类已存在的有参构造
多态
父类引用指向子类对象
#include <iostream>
using namespace std;
class Animal {
public:
void bark() {
cout << "Bark" << endl;
}
};
class Dog : public Animal {
};
int main() {
// 父类引用指向子类对象
Animal *a = new Dog;
Dog dog;
Animal& b = dog;
return 0;
}
多态时,父类对象访问子类(虚函数)
虚函数允许子类重新定义父类成员函数(=>重写)
c++要求在基类中声明的这个函数的时候使用virtual关键字
#include <iostream>
using namespace std;
class Animal {
public:
virtual void bark() {
cout << "Bark" << endl;
}
};
class Dog : public Animal {
public:
void bark() override {
cout << " Dog Bark" << endl;
}
};
int main() {
Animal *a = new Dog;
a->bark();
return 0;
}
case
#include <iostream>
using namespace std;
class KD {
public:
void virtual sendPackage() {
}
};
class SF : public KD {
public:
void sendPackage() override {
cout << "SF快递为您快速发送包裹" << endl;
}
};
class EMS : public KD {
public:
void sendPackage() override {
cout << "EMS快递为您发送包裹,哪里都能送到" << endl;
}
};
class JDL : public KD {
public:
void sendPackage() override {
cout << "JDL为您发送快递,最快当日可达" << endl;
}
};
void send(KD &kd) {
kd.sendPackage();
}
int main() {
SF sf;
JDL jdl;
EMS ems;
send(jdl);
return 0;
}
接口
纯虚函数使用virtual来修饰一个函数,并且实现部分直接设为0
virtual void test() = 0;
如果一个类包含了纯虚函数,那么所在类称为抽象类。
抽象类无法实例化对象,并且子类必须重写父类所有的纯虚函数,否则子类也是抽象类
#include <iostream>
using namespace std;
class KD {
public:
void virtual sendPackage() =0;
};
class SF : public KD {
public:
void sendPackage() override {
cout << "SF快递为您快速发送包裹" << endl;
}
};
class EMS : public KD {
public:
void sendPackage() override {
cout << "EMS快递为您发送包裹,哪里都能送到" << endl;
}
};
class JDL : public KD {
public:
void sendPackage() override {
cout << "JDL为您发送快递,最快当日可达" << endl;
}
};
void send(KD &kd) {
kd.sendPackage();
}
int main() {
SF sf;
JDL jdl;
EMS ems;
send(jdl);
return 0;
}
纯虚函数与多继承
多重继承接口不会带来二义性和复杂问题,接口类只是一个功能声明,并不是功能实现,子类需要根据功能说明定义功能实现。
注意:除了析构函数外,其他声明都是纯虚函数
#include <iostream>
using namespace std;
class Cooker {
public:
virtual void cook() = 0;
virtual void buyFood() =0;
virtual void eat() = 0;
};
class Maid {
public:
virtual void cook()=0;
virtual void wash()=0;
virtual void clean()=0;
};
class Person:public Cooker , public Maid {
public:
void cook() override {
cout<<"Cooking"<<endl;
};
void buyFood() override {
cout<<"Buying"<<endl;
};
void eat() override {
cout<<"Eating"<<endl;
};
void wash() override {
cout<<"Washing"<<endl;
};
void clean() override {
cout<<"Cleaning"<<endl;
};
};
int main() {
return 0;
}
此时 存在二义性
虚析构函数
#include <iostream>
using namespace std;
class Animal {
public:
~Animal() {
cout << "Animal 父类析构函数执行了" << endl;
};
};
class Person: public Animal {
public:
int* n;
Person() {
n = new int(10);
}
~Person() {
cout<<"Person 子类析构函数执行了" << endl;
if (!n) {
delete n;
n = nullptr;
}
}
};
int main() {
Animal *a = new Person();
delete a;
return 0;
}
此时,只会执行父类析构函数。
解决方案:将父类的析构函数作为虚函数,子类的析构函数重写父类的析构函数
#include <iostream>
using namespace std;
class Animal {
public:
virtual ~Animal() {
cout << "Animal 父类析构函数执行了" << endl;
};
};
class Person: public Animal {
public:
int* n;
Person() {
n = new int(10);
}
~Person() override {
cout<<"Person 子类析构函数执行了" << endl;
if (!n) {
delete n;
n = nullptr;
}
}
};
int main() {
Animal *a = new Person();
delete a;
return 0;
}
总结:
- 如果一个类的目的不是为了实现多态,仅仅作为一个基类来使用,那么无需将析构函数设置为虚析构函数
- 如果一个类的目的是为了实现多态,那么这个类的析构函数就有必要设置为虚析构函数
模版
其他语言中的泛型
c++提供两种模版机制:函数模版和类模版
总结:
- 模版把函数或类要处理的数据类型参数化,表现为参数的多态性,成为类属
- 模版用于表达逻辑机构相同,但具体数据元素类型不同的数据对象的通用行为
函数模版
函数模版使用
#include <iostream>
using namespace std;
// 需求:定义一个函数,实现对两个数字相加的功能
template<typename T, typename M>
void add(T n1,M n2) {
cout<<n1+n2<<endl;
}
template<typename T>
void mySwap(T& n1,T& n2) {
T temp=n1;
n1=n2;
n2=temp;
}
class Person {};
int main() {
// 1. 显示指定类型
add<int,double>(1,3);
add<int,int>(1,8);
add<double,double>(1,8);
// 2. 可以根据实参进行类型的自动推导
add(10,3.14);
add(1,8);
add('a',8);
// 3. Person没有进行运算符重载,以下代码报错
// Person p1;
// Person p2;
// add(p1,p2);
return 0;
}
函数模版默认值
template<typename T, typename M = int>
void add(T n1,M n2) {
cout<<n1+n2<<endl;
}
当有虚拟返回值类型,可以放在第一位简化代码调用方式
#include <iostream>
#include <ostream>
using namespace std;
template<typename R,typename T1,typename T2>
R caculate(T1 x,T2 y) {
return (R)(x+y);
}
int main() {
double res = caculate<int>(3,2);
cout << res << endl;
return 0;
}
类模版
使用
#include <iostream>
#include <ostream>
using namespace std;
template<class T1,class T2>
class NumberCalculator {
private:
T1 n1;
T2 n2;
public:
NumberCalculator(){}
NumberCalculator(T1 n1,T2 n2):n1(n1),n2(n2){}
void add() {
cout<<n1+n2<<endl;
}
void sub() {
cout<<n1-n2<<endl;
}
};
int main() {
NumberCalculator<int,int> n1 = NumberCalculator<int,int>(1,2);
n1.add();
n1.sub();
return 0;
}
类模版继承
#include <iostream>
#include <ostream>
using namespace std;
template<class T>
class Animal {
public:
T arg;
};
// 普通类来继承类模版,需要指定父类中的虚拟类型
class Dog : public Animal<int> {};
// 类模版继承类模版
template<class T>
class Cat:public Animal<T> {
};
int main() {
Dog dog;
dog.arg = 10;
cout << dog.arg << endl;
Cat<int> cat;
cat.arg = 10;
cout << cat.arg << endl;
return 0;
}
类外实现
#include <iostream>
#include <ostream>
using namespace std;
template<class T1,class T2>
class NumberCalculator {
private:
T1 num1;
T2 num2;
public:
NumberCalculator();
NumberCalculator(T1 num1,T2 num2);
void add();
};
template<class T1, class T2>
NumberCalculator<T1, T2>::NumberCalculator() {
cout<<"Enter number 1: "<<endl;
}
template<class T1, class T2>
NumberCalculator<T1, T2>::NumberCalculator(T1 num1, T2 num2) {
this.num1 = num1;
this.num2 = num2;
}
template<class T1, class T2>
void NumberCalculator<T1, T2>::add() {
cout<<num1 +num2<<endl;
}
int main() {
return 0;
}
类模版分文件编写
定义和实现部分全部放到一个文件中,这个文件习惯命名为hpp