IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++学习笔记——02 -> 正文阅读

[C++知识库]C++学习笔记——02

一、结构体

结构体是一 群数据类型的集合。它也是一种数据类型。

(一)声明结构体

结构体的名字与类一样,首字母大写。

注意:声明结构体时必须使用struct 修饰,调用、创建结构体变量时可以省略。

  1. 方式一:
#include <iostream>

using namespace std;

struct Person {
    int id = 0; //在声明结构体时可以给结构体中的数据,一个初始值。
    string name = "lihua";
    int age = 18;
    char sex = 'm';
};

int main() {
    Person person1;
    Person person2;
    person1.name = "lihua";
    person2.name = "xiaoming";
    cout << person1.name << endl;
    cout << person2.name << endl;
    return 0;
}
  1. 方式二:
#include <iostream>

using namespace std;

//使用关键字 typedef  给结构体一个别名
typedef  struct Person {
    int id = 0;
    string name = "lihua";
    int age = 18;
    char sex = 'm';
} Student;

int main() {
    Student student1;
    Student student2;
    student1.name = "lihua";
    student2.name = "xiaoming";
    cout << student1.name << endl;
    cout << student2.name << endl;
    return 0;
}

(二)使用结构体

  1. 方式一:
#include <iostream>

using namespace std;

typedef struct Person {
    int id = 0;
    string name = "lihua";
    int age = 18;
    char sex = 'm';
} Student;

int main() {
    Student student;
    student.id = 123;
    student.name = "lihua";
    student.age = 18;
    student.age = 'w';
    cout << student.id << endl;
    cout << student.name << endl;
    cout << student.age << endl;
    cout << student.sex << endl;
    return 0;
}

  1. 方式二:
#include <iostream>

using namespace std;

typedef struct Person {
    int id = 0;
    string name = "lihua";
    int age = 18;
    char sex = 'm';
} Student;

int main() {
	//创建并初始化。
    Student student = {123,"lihua",18,'w'};
    cout << student.id << endl;
    cout << student.name << endl;
    cout << student.age << endl;
    cout << student.sex << endl;
    return 0;
}

(三)结构体数组

  1. 方式一:
#include <iostream>

using namespace std;

typedef struct Person {
    int id = 0;
    string name = "lihua";
    int age = 18;
    char sex = 'm';
} Student;

void printf(const Student &student) {
    cout << student.id << endl;
    cout << student.name << endl;
    cout << student.age << endl;
    cout << student.sex << endl;
}

int main() {
    Student students[3] = {
            {1, "a", 18, 'w'},
            {2, "b", 19, 'm'},
            {3, "c", 20, 'm'}
    };
    for (int i = 0; i < size(students); ++i) {
        printf(students[i]);
    }
    return 0;
}

  1. 方式二:
#include <iostream>

using namespace std;

typedef struct Person {
    int id = 0;
    string name = "lihua";
    int age = 18;
    char sex = 'm';
} Student;

void printf(const Student &student) {
    cout << "id = "<<student.id << " "<< "name = "<<student.name << " "<< "age = "<<student.age << " "<< "sex = "<<student.sex << endl;
}

void printf(Student *student) {
    cout << "id = "<<student->id << " "<< "name = "<<student->name << " "<< "age = "<<student->age << " "<< "sex = "<<student->sex << endl;
}

int main() {
    auto *s = new Student[3]; //等价于Student *s = new Student[3];
    for (int i = 0; i < 3; ++i) {
        printf(s);
        s++;
    }
    return 0;
}

二、内存模型

C++程序在执行时,将内存大方向划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的。注意:代码区的局部变量,不要返回它的指针和引用
  • 全局区:存放全局变量和静态变量以及常量。
  • 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

C++中在程序运行前分为全局区和代码区

  • 代码区特点是共享和只读
  • 全局区中存放全局变量、静态变量、常量
  • 常量区中存放 const修饰的全局常量 和 字符串常量

(一)new 关键字的使用

栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。在C++中主要利用new在堆区开辟内存

new关键字可以在堆区中开辟内存。开辟后的内存程序不会自动释放,当程序结束运行后,操作系统才会回收。

#include <iostream>
#include <windows.h>

using namespace std;


string *useNew() {
    //使用new关键字开辟数据类型为int的内存空间,并初始化值为10;
    int *ip = new int(3);
    cout << *ip << endl;

    //使用new开辟一个float类型的内存空间。
    float *fp  = new float;
    //初始化内存空间存储的值
    *fp = 9.9;
    cout << *fp << endl;

    //使用new开辟大小为3的int数组
    int *array = new int[*ip]{1, 2, 3};
    //遍历
    for (int j = 0; j < 3; ++j) {
        cout << *array << endl;
        array++;
    }
    //创建字符穿变量
    string *str = new string("hello world");
    cout << *str << endl;



    //返回使用new创建的字符的地址。
    return str;
}

int main() {
    string *strP = useNew();
    cout << *strP << endl;
    cout << *strP << endl;
    cout << *strP << endl;
    return 0;
}

在这里插入图片描述

(二)delete 关键字的使用

delete 关键字可以释放有new开辟的内存空间。
释放多个地址,比如:数组 。使用delete [ ] p;

#include <iostream>
#include <windows.h>

using namespace std;


string *useNew() {
    //使用new关键字开辟数据类型为int的内存空间,并初始化值为10;
    int *ip = new int(3);
    cout << *ip << endl;

    //使用new开辟一个float类型的内存空间。
    float *fp = new float;
    //初始化内存空间存储的值
    *fp = 9.9;
    cout << *fp << endl;

    //使用new开辟大小为3的int数组
    int *array = new int[*ip]{1, 2, 3};
    //遍历
    for (int j = 0; j < 3; ++j) {
        cout << *array << endl;
        array++;
    }
    //创建字符穿变量
    string *str = new string("hello world");
    cout << *str << endl;


    //返回使用new创建的字符的地址。
    return str;
}

//使用delete关键字,主动释放new开辟的内存空间
void useDelete(string *strP) {
    //通过指针,指定释放那个地址的内存空间
    delete strP;
}

int main() {
    string *strP = useNew();
    cout << *strP << endl;
    cout << *strP << endl;
    cout << *strP << endl;
    cout << "地址:" << strP << endl;
    useDelete(strP);
    //再次调用会报错
    cout << *strP << endl;


	//释放数组
    int *arrayP = new int[3]{1,2,4};
    delete [] arrayP;
    
    return 0;
}

注释掉cout << *strP << endl;的情况下运行:

在这里插入图片描述

释放内存后继续调用该地址的指针。报错

在这里插入图片描述

(三)如何返回一个局部变量的地址

由C++的内存可以知道,局部变量在函数调用结束后会自动释放内存。因此,不要直接在函数中返回局部变量的地址。

#include <iostream>
#include <windows.h>

using namespace std;

int *test() {
    int a = 10;
    return &a;
}

int main() {

    int *a = test();
    cout << *a << endl;
    cout << *a << endl;
    cout << *a << endl;
    cout << *a << endl;
    return 0;
}

输出:
在这里插入图片描述
除了第一个打印的值外,其他的都是乱码。原因是,函数执行完后,会释放局部变量开辟的内存。至于第一个还能访问成功是c++为了防止用户意外调用,保留了内存的一次访问,之后就释放了内存。

想要返回一个局部变量的地址,必须使用new 关键字开辟空间,然后将这个变量返回。这样在函数调用结束后,才不会被回收内存。

int *test() {
    //错误方式
    //int a = 10;
    //return &a;
    
    //正确方式
    int *a = new int(10);
    return a;
}

三、引用的使用

  1. 引用可以简化指针的使用。
  2. 引用必须初始化,且初始化后,不能改变(指向的内存空间不会改变)。
  3. 引用的本质在c++内部实现是一个指针常量。发现是引用,转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改指向

语法: 数据类型 &别名 = 原名

//引用的使用
#include <iostream>

using namespace std;

void test() {
    //引用必须初始化,且初始化后,不能改变。
    //int &d1 ;
    int test1 = 1;
    int test2 = 2;
    int &d2 = test1;
    d2 = test2; // 注意:这是赋值操作,不是改变引用的指向
    //通过d2引用赋值操作,test1的内容已经发生改变。
    cout << test1 << endl;
    cout << d2 << endl;
}

int main() {
    int a = 10;
    //声明变量时前面用&符号表示:引用(引用直接指向a的地址,修改d,a也会变)
    int &d = a;
    //声明变量时前面用*符号表示:指针(&a表示取a的地址。)
    int *p = &a;
    cout << &a << endl;
    cout << &d << endl;
    cout << p << endl;
    //*p表示获取p指向的内存地址存放的值。
    //cout << *p << endl;
    d = 100;
    cout << a << endl;

    test();

    return 0;
}

注意:引用与指针一样,不能返回局部引用。

//引用的使用
#include <iostream>

using namespace std;

int &errorTest() {
    int a = 10;
    return a;
}

int &correctTest() {
    //静态变量,存放在全局区
    static int a = 100;
    return a;
}

int main() {
    int &test1 = errorTest();
    int &test2 = correctTest();

    //调用一次后,局部变量内存空间地址被释放
    cout << test1 << endl;
    cout << test1 << endl;
    cout << test1 << endl;

    //由于静态变量存放在全局区,所以地址在程序结束运行后才释放,不影响调用。
    cout << test2 << endl;
    cout << test2 << endl;
    cout << test2 << endl;
    return 0;
}

在这里插入图片描述

常量引用 const int& ref = 10; 等价于 const int* const ref = &a; 常量指针常量。它的指向和指向的内容都是不可变的。

四、类

C++面向对象的三大特性为:封装、继承、多态。
封装性与java一致,有三种:public 公共权限、protected 保护权限、private 私有权限。

(一)创建类

创建类,一般在头文件中声明类的属性(变量),方法(函数)。
在源文件中实现类。具体如下:
注意:类名首字母大写。

  1. 头文件:Person.h
//
// Created by wuyuj on 2022/8/26.
//
#include <iostream>
using namespace std;

#ifndef LEARN_01_PERSON_H
#define LEARN_01_PERSON_H


class Person {
private:
    int id;
    string name;
    int age;
    char sex;

public:
    //无参构造函数
    Person();
    //有参构造函数
    Person(int id, const string &name, int age, char sex);
    //析构函数
    virtual ~Person();

    //说出自己的名字
    void say();

    int getId() const;

    void setId(int id);

    const string &getName() const;

    void setName(const string &name);

    int getAge() const;

    void setAge(int age);

    char getSex() const;

    void setSex(char sex);
};


#endif //LEARN_01_PERSON_H

  1. 源文件:Person.cpp
//
// Created by wuyuj on 2022/8/26.
//
#include "Person.h"

void Person::say() {
    cout << "我的名字是:" << name << endl;
}

Person::Person() {
    cout << "调用无参构造函数,创建对象。"  << endl;
}

Person::Person(int id, const string &name, int age, char sex) : id(id), name(name), age(age), sex(sex) {
    cout << "调用有参构造函数,创建对象。"  << endl;
}

Person::~Person() {
    cout << "调用析构函数,对象已经销毁。" <<"析构的对象的id是:"<<this->id << endl;
}

int Person::getId() const {
    return id;
}

void Person::setId(int id) {
    Person::id = id;
}

const string &Person::getName() const {
    return name;
}

void Person::setName(const string &name) {
    Person::name = name;
}

int Person::getAge() const {
    return age;
}

void Person::setAge(int age) {
    Person::age = age;
}

char Person::getSex() const {
    return sex;
}

void Person::setSex(char sex) {
    Person::sex = sex;
}


(二)创建对象

  1. 方式一:像基本类型一样创建对象。类名 变量名; (默认调用无参构造器)
  2. 方式二:显示的调用构造器创建。
  3. 方式三:通过new关键字创建。
#include <iostream>
#include "Person.h"

using namespace std;

int main() {
    //通过new 创建。可以选择构造器。
    auto *person = new Person();
    person->setName("李华");
    person->setId(1);
    cout << "名字为:" << person->getName() << endl;
    //通过new 创建的对象,用完需要销毁,否则造成内存泄漏。如果不销毁等程序运行结束,操作系统才会回收。
    delete person;


    //下面创建的对象,在函数运行结束后,直接销毁。
    //直接创建,默认无参构造函数。
    Person person1;
    person1.setId(2);

    //指定构造函数创建。
    Person person2 = Person();
    person2.setId(3);
    Person person3 = Person(4, "李华", 18, 'm');
    cout << "我的名字是:" << person3.getName() << ",今年:" << person3.getAge() << "岁。" << endl;

    //匿名对象,没有名字的对象,只能使用一次。用完直接析构
    Person(5, "小明", 18, 'm').say();

    return 0;
}

运行结果:

在这里插入图片描述

(三)销毁对象

局部(函数中)创建的对象,在函数结束运行后,自动调用析构函数就行销毁。

全局中的对象需要使用关键字delete 进行 销毁。比如:使用new关键字创建的对象。

(四)class与struct的区别

在C++中 struct和class区别就在于 默认的访问权限不同
区别:

  • struct 默认权限为公共
  • class 默认权限为私有

还有就是class类可以有函数。

在这里插入图片描述

(五)构造函数

构造函数两种分类方式:

  1. ? 按参数分为: 有参构造和无参构造

? 2. 按类型分为: 普通构造和拷贝构造

默认情况下,c++编译器至少给一个类添加3个函数:

  1. 默认构造函数(无参,函数体为空)

  2. 默认析构函数(无参,函数体为空)

  3. 默认拷贝构造函数,对属性进行值拷贝

构造函数默认规则如下:

如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造。

如果用户定义拷贝构造函数,c++不会再提供其他构造函数。

  1. 单参数构造函数,可以隐式调用。一般在构造函数前面 使用explicit 修饰,避免隐式调用。
    下面是隐式调用的例子:
//
// Created by wuyuj on 2022/8/30.
//
#include <iostream>

using namespace std;


class Test {
    int i = 0;
public:
    int p = 0;
    string str = "hello";

    Test(int p) : p(p) {}

    Test(const string &str) : str(str) {}
};

int main() {

    //会隐式调用参数类型匹配的单参构造器。
    Test test = 100;
    cout << test.p << endl;
    
    string name = "lihua";
    Test test1 = name;
    cout << test.str << endl;
    
    return 0;
}

  1. 自己创建有参构造函数后,c++编辑器不再提供无参的。

在这里插入图片描述

  1. 默认的拷贝构造函数(浅拷贝)
//
// Created by wuyuj on 2022/8/30.
//
#include <iostream>

using namespace std;


class Test {
    int i = 0;
public:
    int p = 0;
    string str = "hello";

    Test(int p) : p(p) {}

    Test(const string &str) : str(str) {}
};

int main() {

    //会隐式调用参数类型匹配的单参构造器。
    Test test = 100;
    test.str = "拷贝";
    cout << test.p << endl;

    //使用默认提供的拷贝构造函数,会将test对象的属性的值赋值给test2.
    Test test2 = Test(test);
    cout << test2.p << test2.str << endl;
    return 0;
}

在这里插入图片描述

  1. 浅拷贝和深拷贝
  • 浅拷贝
    demo1:
//
// Created by wuyuj on 2022/8/30.
//
#include <iostream>

using namespace std;


class Test {
    int i = 0;
public:
    int a = 0;
    string str = "hello";
    int *p = nullptr;

    Test() {}

    Test(int a) : a(a) {}

    Test(const string &str) : str(str) {}

};

void copyTest() {
    Test test1;
    test1.a = 10;
    test1.str = "test1";
    test1.p = &test1.a;

    //进行浅拷贝后,test1.p和test2.p的指向了同一块内存,也就是 test1对象的属性a 。
    // 当通过test2.p改变属性a的值后,test1对象的属性a也会受到影响。
    //也就是没有完全拷贝。
    Test test2 = Test(test1);//浅拷贝

    *test2.p = 200;

    cout << "发现对象test1的a属性也发生了改变:" << *test1.p << endl;
}

int main() {
	//浅拷贝
    copyTest();
    return 0;
}

demo2:

//
// Created by wuyuj on 2022/8/30.
//
#include <iostream>

using namespace std;

class Teacher {
public:
    int id = 0;
    string name = "xiaohong";
    int age = 18;

    Teacher() {}

    Teacher(int id, const string &name, int age) : id(id), name(name), age(age) {}
};

class Student {
public:
    int id = 0;
    string name = "lihua";
    int age = 18;
    Teacher *teacher = nullptr;
};

void copyTest() {
    Student student1;
    student1.id = 1;
    student1.name = "小明";
    student1.age = 18;
    Teacher teacher =Teacher(2, "李老师", 45);
    student1.teacher = &teacher;

    //拷贝
    Student student2 = Student(student1);
    student2.teacher->name = "黄老师";
    
    
    //同样发现student1的老师也被改变了。
    cout << student1.teacher->name << endl;
}

int main() {
    //浅拷贝
    copyTest();

    return 0;
}

  • 深拷贝

想要实现深拷贝很简单,只需要我们自定义拷贝函数即可。

//
// Created by wuyuj on 2022/8/30.
//
#include <iostream>

using namespace std;

class Teacher {
public:
    int id = 0;
    string name = "xiaohong";
    int age = 18;

    Teacher() {}

    Teacher(int id, const string &name, int age) : id(id), name(name), age(age) {}

};

class Student {
public:
    int id = 0;
    string name = "lihua";
    int age = 18;
    Teacher *teacher = nullptr;

    Student() {}

    //自定义拷贝构造函数
    Student(const Student &student) {
        id = student.id;
        name = student.name;
        age = student.age;
        //对于是指针类型的属性,需要重新分配内存。让指针指向一块新的内存空间。
        teacher = new Teacher(student.teacher->id, student.teacher->name, student.teacher->age);
    }

    //注意:因为拷贝的对象的teacher属性是通过new创建的,因此需要重写析构函数,释放内存。
    ~Student() {
        //注意:这里释放内存会报错,原因,创建student1的teacher时不是new出来的,因此不能正常释放内存。暂时想不到方法解决
        //delete teacher;
    }
};

void copyTest() {
    Student student1;
    student1.id = 1;
    student1.name = "小明";
    student1.age = 18;
    Teacher teacher = Teacher(2, "李老师", 45);
    student1.teacher = &teacher;

    //拷贝
    Student student2 = Student(student1);
    student2.teacher->name = "黄老师";


    //student1的老师没有被改变。
    cout << student1.teacher->name << endl;
}

int main() {
    //深拷贝
    copyTest();

    return 0;
}

(六)关键字friend(友元)

friend 关键字与java里面的反射有点相似的地方,那就是能访问封装类型为私有的属性和方法。
在类中声明哪些类、方法能访问自己的私有属性和方法。

一共有三种方式:

  1. 全局函数做友元:全局函数中能调用该类的私有属性和方法。
  2. 类做友元:类中能调用其他类的私有属性和方法。
  3. 成员函数做友元:成员函数中能调用其他类的私有属性和方法。
//friend关键字的使用
#include <iostream>

using namespace std;

class Student;
class Teacher;


class Student {
    //方式一:告诉编译器 global全局函数 是 Building类的好朋友,可以访问类中的私有内容
    friend void global(Student & student);

    //方式二:通过friend修饰其他类,那么在这个类中就能调用私有变量和方法。
    friend class Teacher;


private:
    int money = 100;
    void myMoney() const {
        cout << "我有这么多钱:" << money << endl;
    }

public:
    int id;
    string name;
    int age;
};

class Teacher{
private:
    int id;
    string name;
    int age;
public:
    void getStudent(){
        Student student;
        //方式二:通过friend修饰其他类,那么在这个类中就能调用它的私有变量和方法。
        student.myMoney();
    }
};



//方式一:通过friend 修饰全局函数,调用私有变量和方法。
void global(Student & student){
    cout << student.money<< endl;
    student.myMoney();
}



class Test2{
public:
    void getTest1();
};

class Test1{
    //方式三:通过friend修饰其他类的成员方法,那么在这个方法中就能调用私有变量和方法。
    friend void Test2::getTest1();
private:
    int a = 10;
};

//方式三:通过friend修饰其他类的成员方法,那么在这个方法中就能调用私有变量和方法。
void Test2::getTest1(){
    Test1 test1;
    cout <<"方式三:"<<test1.a <<endl;
}



int main() {
    Student student;
    //方式一:通过friend 修饰全局函数,调用私有变量和方法。
    global(student);

    //方式二:在一个类中通过friend,修饰其他类,那么在这个类中就能调用私有变量和方法。
    Teacher teacher;
    teacher.getStudent();

    //方式三:通过friend修饰其他类的成员方法,那么在这个方法中就能调用私有变量和方法。
    Test2 test2;
    test2.getTest1();
    return 0;
}

(七)对象关系运算符重载 operator关键字的使用

让对象之间也能进行相加,比较等操作。

//对象关系运算符重载 operator关键字的使用
#include <iostream>
#include <utility>

using namespace std;

class Person {
private:
    int id = 0;
    int age = 18;
    int money = 100;
    string name = "lihua";
public:
    Person() = default;

    Person(int id, int age, int money, string name) : id(id), age(age), money(money), name(std::move(name)) {}

    //成员函数实现 + 号运算符重载
    Person operator+(const Person &p) const {
        Person temp;
        temp.id = id + p.id;
        temp.age = age + p.age;
        temp.money = money + p.money;
        temp.name = name +"-"+ p.name;
        return temp;
    }

    // 重写==
    bool operator==(const Person &p) const {
        return money == p.money;
    }
    //可以重写各种运算符。



    int getId() const {
        return id;
    }

    void setId(int id) {
        Person::id = id;
    }

    int getAge() const {
        return age;
    }

    void setAge(int age) {
        Person::age = age;
    }

    int getMoney() const {
        return money;
    }

    void setMoney(int money) {
        Person::money = money;
    }

    const string &getName() const {
        return name;
    }

    void setName(const string &name) {
        Person::name = name;
    }
};

void printf(Person &person){
    cout << "id="<<person.getId()<<" ;"<< "age="<<person.getAge()<<" ;"<< "name="<<person.getName()<<" ;"<< "money="<<person.getMoney()<<endl;
}

int main() {
    Person person1 = Person(1, 18, 100, "li");
    Person person2 = Person(2, 20, 100, "feng");
    Person p = person1 + person2;
    printf(p);
    if (person1 == person2) {
        cout << "两者的钱一样" << endl;
    }
    return 0;
}

更多可以参考这篇文章

(八)继承

1. 如何继承

类继承的格式 class 子类 : public 父类 ; public 表示继承父类那些封装类型的属性和方法,也就是继承类型。一共有三种继承类型:公共继承、保护继承、私有继承。

//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : public Father {
private:
    string hobby;
    string school;
public:
    void mySchool() {
        cout << "我在" << school << "上学。" << endl;
    }

    const string &getHobby() const {
        return hobby;
    }

    void setHobby(const string &hobby) {
        Son::hobby = hobby;
    }

    const string &getSchool() const {
        return school;
    }

    void setSchool(const string &school) {
        Son::school = school;
    }
};


int main() {

    Son son;
    son.name = "小明";
    son.money = 0;
    son.age = 18;
    //可以调用父类的方法。
    son.say();
    son.setSchool("星星小学");
    son.mySchool();
    return 0;
}

2. 继承类型

三种继承方式:

  • 公共继承:子类内只能访问父类封装类型为 public 的属性和方法。同时子类继承到的属性方法的封装类型变为:public。
  • 保护继承:子类内能访问父类封装类型为 public和protected 的属性和方法。同时子类继承到的属性方法的封装类型变为:protected。
  • 私有继承:子类内能访问父类封装类型为 public和protected 的属性和方法。同时子类继承到的属性方法的封装类型变为:private。
  1. public
//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
    const string &getHobby() const {
        return hobby;
    }
protected:
    string house;
    const string &getHouse() const {
        return house;
    }
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : public Father {
public:
    void test(){
       Son son;
       //son.
    }
};


int main() {
    Son son;

    return 0;
}

在这里插入图片描述

  1. protected
//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
    const string &getHobby() const {
        return hobby;
    }
protected:
    string house;
    const string &getHouse() const {
        return house;
    }
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : protected Father {
public:
    void test(){
       Son son;
       //son.
    }
};

在这里插入图片描述

  1. private
//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
    const string &getHobby() const {
        return hobby;
    }
protected:
    string house;
    const string &getHouse() const {
        return house;
    }
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : private Father {
public:
    void test(){
       Son son;
       son.
    }
};

在这里插入图片描述

3. 继承后,子类和父类的构造函数和析构函数调用顺序

对于构造函数,先调用父类再调用子类。

//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
    const string &getHobby() const {
        return hobby;
    }
protected:
    string house;
    const string &getHouse() const {
        return house;
    }
public:
    string name;
    int age;
    int money;

    Father() {
        cout << "父类构造函数。" << endl;
    }

    virtual ~Father() {
        cout << "父类析构函数。" << endl;
    }

    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : private Father {
public:
    Son() {
        cout << "子类构造函数。" << endl;
    }

    virtual ~Son() {
        cout << "子类析构函数。" << endl;
    }
};


int main() {
    Son son;
    
    return 0;
}

在这里插入图片描述

4. 重写父类的函数(函数、变量重名)

  1. 当子类有函数与父类一样时,子类会重写父类提供的函数。(默认用自己写的,不用父类提供的)
//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
    const string &getHobby() const {
        return hobby;
    }
protected:
    string house;
    const string &getHouse() const {
        return house;
    }
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : public Father {
public:
    //重写say函数
    void say() const {
        cout << "子类-》我的名字是" << name << endl;
    }
};


int main() {
    Son son;
    son.say();
    return 0;
}

发现调用的是子类的say函数。

在这里插入图片描述

想要调用父类的怎么办?只需要在函数前面标明是父类的。son.Father::say();

//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
    const string &getHobby() const {
        return hobby;
    }
protected:
    string house;
    const string &getHouse() const {
        return house;
    }
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : public Father {
public:
    //重写say函数
    void say() const {
        cout << "子类-》我的名字是" << name << endl;
    }
};


int main() {
    Son son;
    son.Father::say();
    return 0;
}

5. 多重继承

c++中是能多重继承的,也就是子类可以有多个父类。

格式:class 子类 :封装类型 父类1 , 封装类型 父类2 。比如:class Son : public Father1,public Father2 ;

注意:多重继承,父类中可能出现一样的方法和变量,所以要加前缀区分。

//继承学习
#include <iostream>

using namespace std;


class Father1 {
private:
    string hobby;
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Father2 {
private:
    string hobby;
public:
    string name;
    int age;
    int money;
    void say() const {
        cout << "我的名字是" << name << endl;
    }
};

class Son : public Father1,public Father2 {
public:
    void test() const {
        //当函数、变量名一样时,用前缀加以区分。
        cout << "子类-》我的名字是" << Father1::name << endl;
        Father2::say();
    }
};


int main() {
    Son son;
    son.test();
    //当函数、变量名一样时,用前缀加以区分。
    son.Father1::say();
    return 0;
}

6. 菱形继承

菱形继承概念:

? 两个派生类A、B继承同一个基类F

? 又有某个类C同时继承者两个派生类A、B。(这时候C就会间接继承两个F。有两个一样的爷爷。因此在调用时,就不知道调用哪个爷爷。)

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

可以使用 virtual 关键字解决上面产生的问题。

class Animal
{
public:
	int m_Age;
};

//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo   : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};

void test01()
{
	SheepTuo st;
	st.Sheep::m_Age = 100;
	st.Tuo::m_Age = 200;

	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
	cout << "st.m_Age = " << st.m_Age << endl;
}


int main() {

	test01();

	system("pause");

	return 0;
}

7. 多态

什么是多态?简单来说就是,将一个子类的对象,向上转型为父类。比如 :Father father = Son(); 。这样将Father father作为函数的参数,那么在调用函数时,可以传入各种各样的子类的对象

多态满足条件

  • 有继承关系
  • 子类重写父类中的虚函数

多态使用条件

  • 父类指针或引用指向子类对象
//继承学习
#include <iostream>

using namespace std;


class Father {
private:
    string hobby;
public:
    string name;
    int age;
    int money;

    virtual void beat() const {
        cout << "我的名字是" << name << endl;
    }
};


class Son1 : public Father {
public:
    void beat() const override {
        cout << "Son1-> 被打" << endl;
    }
};

class Son2 : public Father {
public:
    void beat() const override {
        cout << "Son2-> 被打" << endl;
    }
};


//父亲打儿子,可以选择打哪个儿子。
void beatSon(const Father& son){
    son.beat();
}

int main() {
    Son1 son1;
    beatSon(son1);

    Son2 son2;
    beatSon(son2);

    //当函数、变量名一样时,用前缀加以区分。
    return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 00:51:43  更:2022-09-04 00:56:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 11:43:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码