C++类和对象3

简介: C++类和对象

C++类和对象2:https://developer.aliyun.com/article/1548157

五、运算符重载

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型;

1、加号运算符重载

#include <iostream>
#include <string>
 
using namespace std;
 
class Person {
public:
    int m_A;
    int m_B;
//成员函数加号重载
    Person operator+(const Person &person) {
        Person tem;
        tem.m_A = this->m_A + person.m_A;
        tem.m_B = this->m_B + person.m_B;
        return tem;
    };
};
//成员函数加号重载
Person operator+(const Person &p1,const Person &p2) {
    Person tem;
    tem.m_A = p1.m_A + p2.m_A;
    tem.m_B = p1.m_B + p2.m_B;
    return tem;
};
void test01() {
    Person p1;
    p1.m_A = 10;
    p1.m_B = 20;
    Person p2;
    p2.m_A = 10;
    p2.m_B = 20;
    Person p3 = p1 + p2;
    cout << p3.m_A << endl;
    cout << p3.m_B << endl;
}
 
int main() {
    test01();
    return 0;
}
 
 


20
40

2、左移运算符重载

重载左移运算符配合友元可以实现输出自定义数据类型

#include <iostream>
#include <string>
 
using namespace std;
 
//左移运算符重载
class Person {
    friend ostream &operator<<(ostream &cout, Person &p);
 
public:
    Person(int a, int b) : m_A(a), m_B(b) {}
 
private:
    int m_A;
    int m_B;
};
 
//只能利用全局函数重载左移运算符 成员函数无法实现cout在左侧
ostream &operator<<(ostream &cout, Person &p) {
    cout << "m_A=" << p.m_A << " m_B=" << p.m_B;
    return cout;
}
 
void test01() {
    Person p(10, 10);
    cout << p << " test" << endl;
}
 
int main() {
    test01();
    return 0;
}
 
 
m_A=10 m_B=10 test

3、递增运算符重载

前置递增返回的是引用,后置递增返回的值。

#include <iostream>
#include <string>
 
using namespace std;
 
//自增运算符重载
class MyInteger {
    friend ostream &operator<<(ostream &cout, const MyInteger &myInteger);
 
public:
    MyInteger() {
        m_Num = 0;
    }
 
    //重载前置++运算符
    MyInteger &operator++() {
        // 先进行++运算
        m_Num++;
        //再将自身做返回
        return *this;
    }
 
    //重载后置++运算符
    MyInteger operator++(const int) {
        //        先记录当时的结果
        MyInteger tem = *this;
        // 后递增
        m_Num++;
        //最后将记录结果做返回
        return tem;
    }
 
private:
    int m_Num;
};
 
ostream &operator<<(ostream &cout, const MyInteger &myInteger) {
    cout << myInteger.m_Num << " ";
    return cout;
}
 
void test01() {
    int a = 10;
    cout << ++a << endl;
    cout << a << endl;
    int b = 10;
    cout << b++ << endl;
    cout << b << endl;
}
 
void test02() {
    MyInteger myInt;
    cout << ++(++myInt) << endl;
    cout << myInt << endl;
}
 
void test03() {
    MyInteger myInt;
    cout << myInt++ << endl;
    cout << myInt << endl;
}
 
int main() {
//    test01();
    test02();
    test03();
    return 0;
}
 
 
2
2
0
1

4、赋值运算符重载

#include <iostream>
#include <string>
 
using namespace std;
 
//赋值运算符重载
class Person {
public:
    Person(int age) {
        m_Age = new int(age);
    }
 
    ~Person() {
        if (m_Age != NULL) {
            delete m_Age;
            m_Age = NULL;
        }
    }
 
//    重载赋值运算符
    Person &operator=(Person &p) {
//        先判断是否有属性在堆区,如果有先释放,然后深拷贝
        if (m_Age != NULL) {
            delete m_Age;
            m_Age = NULL;
        }
        m_Age = new int(*p.m_Age);
        return *this;
    }
 
    int *m_Age;
};
 
void test01() {
    Person p1(18);
    Person p2(20);
    Person p3(30);
    p3=p2 = p1;
    cout << *p1.m_Age << endl;
    cout << *p2.m_Age << endl;
    cout << *p3.m_Age << endl;
}
 
int main() {
    test01();
    return 0;
}
 
 


18
18
18

5、关系运算符重载

作用: 重载关系运算符,可以让两个自定义类型对象进行对比操作

#include <iostream>
#include <string>
 
using namespace std;
 
//作用: 重载关系运算符,可以让两个自定义类型对象进行对比操作
class Person {
public:
    Person(string name, int age) {
        name = m_Name;
        age = m_Age;
    }
 
    bool operator==(const Person &p) {
        if (this->m_Name == p.m_Name&&this->m_Age==p.m_Age) {
            return true;
        } else {
            return false;
        }
    }
    bool operator!=(const Person &p) {
        return !operator==(p);
    }
 
    string m_Name;
    int m_Age;
};
 
void test01() {
    Person p1("zhang", 15);
    Person p2("zhang", 16);
    cout << (p1 == p2) << endl;
}
 
int main() {
    test01();
    return 0;
}
 
 
0

6、函数调用运算符重载

函数调用运算符()也可以重载

由于重载后使用的方式非常像函数的调用,因此称为仿函数

仿函数没有固定写法,非常灵活        

#include <iostream>
#include <string>
 
using namespace std;
 
//函数调用运算符()也可以重载
//由于重载后使用的方式非常像函数的调用,因此称为仿函数
//仿函数没有固定写法,非常灵活
class MyPrint {
public:
    void operator()(string text) {
        cout << text << endl;
    }
};
 
void test01() {
//    重载()操作符,也称为仿函数
    MyPrint myPrint;
    myPrint("hello world");
//    匿名函数
    MyPrint()("hello world");
}
 
int main() {
    test01();
    return 0;
}
 
 
hello world
hello world

六、继承

1、继承的基本语法

继承好处:减少重复代码

语法:class 子类:继承方式 父类

子类也称为派生类

父类也称为基类

#include <iostream>
#include <string>
 
using namespace std;
/*
 * 继承好处:减少重复代码
 * 语法:class 子类:继承方式 父类
 * 子类也称为派生类
 * 父类也称为基类
 */
//普通页面实现
class BasePage {
public:
    void header() {
        cout << "首页、公开课、登录、注册……(公共头部)" << endl;
    }
 
    void footer() {
        cout << "帮助中心、交流合作、站内地图……(公共底部)" << endl;
    }
 
    void left() {
        cout << "Java、Python、C++……(公共分类列表)" << endl;
    }
};
 
//java页面
class Java : public BasePage {
public:
    void content() {
        cout << "Java学科视频" << endl;
    }
};
 
//python页面
class Python : public BasePage {
public:
    void content() {
        cout << "Python学科视频" << endl;
    }
};
 
void test01() {
    cout << "Java下载视频" << endl;
    Java ja;
    ja.header();
    ja.footer();
    ja.left();
    ja.content();
    cout<<"------------------"<<endl;
    cout << "Python下载视频" << endl;
    Python py;
    py.header();
    py.footer();
    py.left();
    py.content();
}
 
int main() {
    test01();
    return 0;
}
 
 


Java下载视频
首页、公开课、登录、注册……(公共头部)
帮助中心、交流合作、站内地图……(公共底部)
Java、Python、C++……(公共分类列表)
Java学科视频
------------------
Python下载视频
首页、公开课、登录、注册……(公共头部)
帮助中心、交流合作、站内地图……(公共底部)
Java、Python、C++……(公共分类列表)
Python学科视频

2、继承方式

公共继承:父类的私有属性不可访问,其他属性权限和父类保持一致;

保护继承:父类的私有属性不可访问,父类public权限会降级为protected;

私有继承:父类的私有属性不可访问,父类public、protected会降级为private;

#include <iostream>
#include <string>
 
using namespace std;
 
//公共继承基类
class Base1 {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};
//公共继承
class Son1:public Base1{
public:
    void func(){
        cout<<m_A<<endl;
        cout<<m_B<<endl;
    }
};
//保护继承
class Son2:protected Base1{
    void func(){
        cout<<m_A<<endl;
        cout<<m_B<<endl;
    }
};
//私有继承
class Son3:private Base1{
    void func(){
        cout<<m_A<<endl;
        cout<<m_B<<endl;
    }
};
 
void test01(){
    Son1 s1;
    s1.m_A=100;
//    s1.m_B=100;
}
int main() {
 
    return 0;
}
 
 

3、继承中的对象模型

#include <iostream>
#include <string>
 
using namespace std;
 
//作用: 重载关系运算符,可以让两个自定义类型对象进行对比操作
class Base {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;//私有成员只是被隐藏,但是还是会被继承下去
};
 
//公共继承
class Son : public Base {
public:
    int m_D;
};
 
void test01() {
    //16
    //父类中所有非静态成员属性都会被子类继承下去
    //父类中私有成员属性是被编译器给隐藏了,因此访问不到,但是确实被继承下去了
    cout << "size of Son = " << sizeof(Son) << endl;
}
 
int main() {
    test01();
    return 0;
}
 
  
size of Son = 16

使用*Visual Studio 2022 Developer Command Prompt v17.5.5工具,查看对象模型;

#进入代码盘符
D:\ProgramFiles\Microsoft Visual Studio\2022\Community>f:
#进入代码目录
 
F:\>cd F:\myCode\c++\0418_01
#查看代码类的结构  cl /d1 reportSingleClassLayout类名 文件名
 
F:\myCode\c++\0418_01>cl /d1 reportSingleClassLayoutSon main.cpp

4、继承中构造与析构顺序

先构造父类,再构造子类,析构的顺序与构造的顺序相反

#include <iostream>
#include <string>
 
using namespace std;
 
//继承中的构造与析构顺序
class Base {
public:
    Base() {
        cout << "Base构造执行" << endl;
    }
 
    ~Base() {
        cout << "Base析构执行" << endl;
    }
};
 
class Son : public Base {
public:
    Son() {
        cout << "Son构造执行" << endl;
    }
 
    ~Son() {
        cout << "Son析构执行" << endl;
    }
};
 
void test01() {
    Son s;
}
 
//继承中的构造和析构顺序如下:
//先构造父类,再构造子类,析构的顺序与构造的顺序相反
int main() {
    test01();
    system("pause");
    return 0;
}
Base构造执行
Son构造执行
Son析构执行
Base析构执行

5、继承同名成员处理方式

访问子类同名成员 直接访问即可

访问父类同名成员 需要加作用域

#include <iostream>
#include <string>
 
using namespace std;
 
//访问子类同名成员 直接访问即可
//访问父类同名成员 需要加作用域
class Base {
public:
    Base() {
        m_A = 100;
    }
 
    void func() {
        cout << "Base func()" << endl;
    }
 
    void func(int a) {
        cout << "Base func(int a)" << endl;
    }
 
    int m_A;
};
 
class Son : public Base {
public:
    Son() {
        m_A = 200;
    }
 
    void func() {
        cout << "Son func()" << endl;
    }
 
    int m_A;
};
 
//同名成员属性
void test01() {
    Son son;
    cout << son.m_A << endl;
//    如果通过子类对象访问父类中的同名成员,需要加作用域
    cout << son.Base::m_A << endl;
};
 
//同名成员函数
void test02() {
    Son son;
    son.func();
    son.Base::func();
//    如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏父类中所有同名成员函数,如果想访问,需要加作用域
    son.Base::func(10);
};
 
int main() {
    test01();
    test02();
    return 0;
}
 
 


200
100
Son func()
Base func()

6、继承同名静态成员处理方式

与同名成员处理方式一致。可通过类名访问。

7、多继承语法

#include <iostream>
#include <string>
 
using namespace std;
 
//多继承
class Base1 {
};
 
class Base2 {
};
 
class Son : public Base1, protected Base2 {
 
};
 
int main() {
 
    return 0;
}
 
 

8、菱形继承

两个派生类继承同一个基类

又有某个类同时继承者两个派生类

这种继承被称为菱形继承,或者钻石继承

#include <iostream>
#include <string>
 
using namespace std;
 
//动物类
class Animail {
public:
    int m_Age;
};
 
//利用虚继承 解决菱形继承的问题
//继承之前加上 virtual变成虚继承 Animail类称为虚基类
//羊类
class Sheep : virtual public Animail {
};
 
//驼类
class Tuo : virtual public Animail {
};
 
//羊驼类
class SheepTuo : public Sheep, public Tuo {
};
 
void test01() {
    SheepTuo st;
    st.Sheep::m_Age=18;
    st.Tuo::m_Age=28;
    cout<<st.m_Age<<endl;
    cout << sizeof(st) << endl;
}
 
int main() {
    test01();
    return 0;
}
 
 
28
24

七、多态

静态多态:函数重载和 运算符重载属于静态多态,复用函数名;

动态多态: 派生类和虚函数实现运行时多态;

静态多态的函数地址早绑定 - 编译阶段确定函数地址;

动态多态的函数地址晚绑定 - 运行阶段确定函数地址;

#include <iostream>
#include <string>
 
using namespace std;
 
//多态
 
//动物类
class Animal {
public:
    //虚函数
    virtual void speak() {
        cout << "Animal speak" << endl;
    }
};
 
class Cat : public Animal {
public:
    void speak() {
        cout << "cat apeak" << endl;
    }
};
 
//执行说话的函数
//地址早绑定,在编译阶段确定函数地址
//如果想让猫说话,那么这个函数地址不能提前绑定,需要在运行阶段绑定,地址晚绑定
//动态多态满足条件:1、有继承关系;2、子类重写父类的虚函数。动态多态的使用:父类的指针或者引用,指向子类对象。
void doSpeak(Animal &animal) {
    animal.speak();
}
 
void test01() {
    Cat cat;
    doSpeak(cat);
}
 
int main() {
    test01();
    return 0;
}
 
 

代码组织结构清晰

可读性强

利于前期和后期的扩展以及维护

#include <iostream>
#include <string>
 
using namespace std;
 
//计算器普通写法
class Calculator {
public:
    int getResult(string oper) {
        if (oper == "+") {
            return m_NUm1 + m_Num2;
        } else if (oper == "-") {
            return m_NUm1 - m_Num2;
        } else if (oper == "/") {
            return m_NUm1 / m_Num2;
        } else if (oper == "*") {
            return m_Num2 * m_NUm1;
        }
 
    }
 
    int m_NUm1;
    int m_Num2;
};
 
//实现计算器抽象类
class AbstractCalculator {
public:
    virtual int getResult() {
        return 0;
    }
 
    int m_Num1;
    int m_Num2;
};
 
//加法计算器
class AddCalculator : public AbstractCalculator {
public:
    int getResult() {
        return m_Num1 + m_Num2;
    }
};
 
//减法计算器
class SubCalculator : public AbstractCalculator {
public:
    int getResult() {
        return m_Num1 - m_Num2;
    }
};
 
void test01() {
    Calculator cal;
    cal.m_NUm1 = 10;
    cal.m_Num2 = 20;
    cout << cal.getResult("+") << endl;
    cout << cal.getResult("-") << endl;
}
 
void test02() {
    AbstractCalculator *abc = new AddCalculator();
    abc->m_Num1 = 10;
    abc->m_Num2 = 20;
    cout << abc->getResult() << endl;
    delete abc;
}
 
int main() {
//    test01();
    test02();
    return 0;
}
 
 

类中有纯虚函数,类称为抽象类;

抽象类无法实例化对象,子类必须重写抽象类的纯虚函数,否则也称为抽象类;

#include <iostream>
#include <string>
 
using namespace std;
 
//类中有纯虚函数,类称为抽象类
//抽象类无法实例化对象,子类必须重写抽象类的纯虚函数,否则也称为抽象类
class Base {
public:
    virtual void func() = 0;
};
 
class Son : public Base {
public:
    void func() {
        cout << "son func" << endl;
    }
};
 
void test01() {
    Base *base = new Son();
    base->func();
    delete base;
}
 
int main() {
    test01();
    return 0;
}
 
 

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码

解决方式:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:都可以解决父类指针释放子类对象,都需要有具体的函数实现;区别:纯虚析构的类输入抽象类,无法实例化对象。

#include <iostream>
#include <string>
 
using namespace std;
 
//多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码
//解决方式:将父类中的析构函数改为虚析构或者纯虚析构
//虚析构和纯虚析构共性:都可以解决父类指针释放子类对象,都需要有具体的函数实现;区别:纯虚析构的类输入抽象类,无法实例化对象。
 
//虚析构和纯虚析构
class Animal {
public:
    Animal() {
        cout << "Animal 构造函数" << endl;
    }
 
//利用虚析构可以解决,父类指针释放子类对象时不干净的问题。
//  virtual  ~Animal() {
//        cout << "Animal 析构函数" << endl;
//    }
//纯虚析构,需要申明和实现
    virtual  ~Animal() = 0;
 
    //    纯虚函数
    virtual void speak() = 0;
};
 
Animal::~Animal() {
    cout << "Animal 纯虚析构函数" << endl;
}
 
class Cat : public Animal {
public:
    Cat(string name) {
        cout << "Cat 构造函数" << endl;
        m_Name = new string(name);
    }
 
    virtual void speak() {
        cout << *m_Name << "小猫在说话" << endl;
    }
 
    ~Cat() {
        if (m_Name != NULL) {
            cout << "Cat 析构函数" << endl;
            delete m_Name;
            m_Name = NULL;
        }
    }
 
    string *m_Name;
};
 
void test01() {
    Animal *animal = new Cat("tom ");
    animal->speak();
    delete animal;
}
 
int main() {
    test01();
    return 0;
}
 
 

Animal 构造函数
Cat 构造函数
tom 小猫在说话
Cat 析构函数
Animal 纯虚析构函数
相关文章
|
1月前
|
编译器 C++
C++之类与对象(完结撒花篇)(上)
C++之类与对象(完结撒花篇)(上)
35 0
|
10天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
39 4
|
11天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
35 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
17 0
|
1月前
|
编译器 C++ 数据库管理
C++之类与对象(完结撒花篇)(下)
C++之类与对象(完结撒花篇)(下)
31 0
|
1月前
|
编译器 C++
C++之类与对象(3)(下)
C++之类与对象(3)(下)
34 0
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)