C++语言核心知识梳理

一、认识C++

1.1、C++与C语言的设计差异

C++简介:C++是C的拓展,早期叫做“带类的C”,它增加了很多新的语法,目的是提高开发效率。因此学了C语言就相当于学了 C++ 的一半,从C语言转向 C++ 时,不需要再从头开始,接着C语言往下学就可以。

C++与C的设计差异

  1. 文件组织体系

C++加入了命名空间(类似于JAVA包体系),命名空间的出现是为了更好的组织代码

  1. 加入了业务组织体系

C++加入了class OOP概念,为业务服务,而C所有东西都要基于内存处理

  1. 加入了一些运算符和符号

::域访问操作符

-> 指针的指向运算符,通常用在结构体中

& 取别名(C++的引用就是别名,引用在本质上属于指针)

1.2、命名空间namespace

命名空间类似于Java中的Package包的概念,类似于 import package;

其中::访问修饰符

#include 
int main()
{
    std::cout << "Hello World!\n";//Hello World!
}

使用命名空间可以省略std::

#include 
//标准命名空间standard(包含很多标准的定义)
using namespace std;

int main()
{
    cout << "Hello World!\n";//Hello World!
}

自定义命名空间与命名空间嵌套

//自定义命名空间
namespace NSP_A {
    int a = 9;
    struct Teacher {
        char name[20];
        int age;
    };
    struct Student {
        char name[20];
        int age;
    };
}

namespace NSP_B {
    int a = 12;
    //命名空间嵌套
    namespace NSP_C {
        int c = 90;
    }
}

int main()
{
    std::cout << NSP_A::a << std::endl;//9
    std::cout << NSP_B::NSP_C::c << std::endl;//90
}

使用命名空间结构体的两种写法

//使用命名空间中的结构体
NSP_A::Student s;
//    using NSP_A::Student;
//    Student s;
s.age = 19;

1.3、C++创建对象内存分配

C++创建对象分配与java不同

Java声明对象不会开辟内存,在new的时候才会开辟内存。

C++在声明对象就开辟内存了,如果是指针则开辟指针空间大小,如果是类对象则直接开辟类对象所需大小,详细参考

二、类

2.1、简单实现

#include 
using namespace std;
#define PI 3.14

class MyCircle {
private:
    double r;
    double s;
public:
    void setR(double r) {
        this->r = r;
    }
    //获取面积
    double getS() {
        return PI * r * r;
    }
};

int main() {
    MyCircle c1;
    c1.setR(4);
    cout << "圆的面积:" << c1.getS() << endl;
    return 0;
}

也可以先定义,再通过域访问操作符实现函数体

class MyCircle {
private:
    double r;
    double s;
public:
    void setR(double r) {
        this->r = r;
    }
    //获取面积
    double getS();
};
//也可以先定义再实现
double MyCircle::getS()
{
    return PI * r * r;
}

2.2、类对象、类指针区别

区别 声明方式 大小 访问方式
类对象 book bb; 内容的大小 .(点)运算符
类指针 book *b; 固定的函数指针大小8 ->指针箭头函数操作
class book {
public:
    void set_name(string n) {
        this->name = n;
    }

    void print_book() {
        cout << "This book's name is " << this->name << endl;
    }

private:
    string name;
};
int main() {
    // 1、类对象,后面的 = *new book()可省略
//    book bb = *new book();
    book bb;
    cout << new book() << endl;//有内容
    cout << sizeof bb << endl;//打印:24,打印出来是内容大小
    //.(点)运算符
    bb.set_name("zhangsan");
    bb.print_book();//T打印:his book's name is zhangsan

    // 2、类指针
    book *b;
    cout << sizeof b << endl;//打印:8,打印出来是地址大小
    //->指针操作
    b->set_name("zhangsan");
    b->print_book();//打印:This book's name is zhangsan
}

三、结构体

在c++中结构体没有类的解构、析构等方法,因此没有类方便

#include 
using namespace std;

struct MyTeacher {
public:
    char name[20];
    int age;
public:
    void say() {
        cout << this->age << "岁" << endl;
    }
};
int main() {
    MyTeacher t1;
    t1.age = 10;
    t1.say();//10岁
    return 0;
}

四、布尔类型

布尔类型:为false的情况:0、false、NULL

大小:占一个字节

#include 
using namespace std;

int main() {
    //bool isSingle = true;
    bool isSingle = 17;
    if (isSingle) {
        cout << "单身" << endl;//单身
        cout << sizeof(bool) << endl;//1
    }
    else {
        cout << "有对象" << endl;
    }

    int a = 10, b = 20;
    ((a > b) ? a : b) = 30;
    cout << b << endl;//30
    return 0;
}

五、别名

5.1、声明方式

别名的声明方式int& b = a,别名可以有多个

#include 
using namespace std;

int main() {
    //变量名-门牌号(内存空间0x00001的别名,可不可以有多个名字?)
    int a = 9;
    //b就这个内存空间另外一个别名
    //& C++中的引用
    int& b = a;
    cout << b << endl;
    return 0;
}

5.2、值交换demo

//指针值交换
void swap_1(int *a,int *b){
    int tmp=*a;;
    *a=*b;
    *b=tmp;
}

//引用值交换
void swap_2(int& a,int& b){
    int tmp=a;
    a=b;
    b=tmp;
}

int main(){
    int x=10;
    int y=20;
    printf("%d,%d\n",x,y);//10,20
    swap_1(&x, &y);
//    swap_2(x, y);
    printf("%d,%d\n",x,y);//20,10
    return 0;
}

5.3、方法打印demo

struct Teacher{
    char *name;
    int age;
};
void myprint(Teacher& t){
    cout<name<<","<age<

5.4、作为函数的参数或返回值

struct Teacher{
    char* name;
    int age;
};

void myprint(Teacher& t){
    cout<name<<","<age<

5.5、指针的引用,代替二级指针

//二级指针
void getTeacher(Teacher **p){
    Teacher* tmp=(Teacher*)malloc(sizeof(Teacher));
    tmp->age=20;
    *p=tmp;
}

//别名
void getTeacher(Teacher* &p){
    p=(Teacher*)malloc(sizeof(Teacher));
    p->age=20;
}

int main(){
    Teacher* t=NULL;
    getTeacher(&t);
//    getTeacher(t);
    cout<age<

5.6、指针常量与常量指针

int main() {
    int a = 2, b = 3;
    //指针常量,指针的常量,不改变地址的指针,但是可以修改它指向的内容
    int *const p1 = &a;
    //p1=&b;//不允许Cannot assign to variable 'p1' with const-qualified type 'int *con
    *p1 = 4;//允许

    //常量指针,指向常量的指针,内容不能修改
    const int *p2 = &a;
    p2 = &b;//允许
//    *p2=4;//不允许Read-only variable is not assignable

    //会锁定内容
    cout << *p2 << endl;//3
    a=33;
    cout << *p2 << endl;//3
    return 0;
}

函数参数指定为常量指针

void myprint(const int& a){
    cout<

5.7、常引用

int main(){
    // 引用必须要有值,不能为空
//    int& a=NULL;//不允许Non-const lvalue reference to type 'int' cannot bind to a temporary of type 'long'
    
    //常引用
    //常引用类似于java中final
    int a=10,b=9;
    const int& c=a;
//    c=b;//不允许Cannot assign to variable 'c' with const-qualified type 'const int &'
    
    //字面量
    const int& d=70;
    return 0;
}

5.8、引用大小

struct Teacher{
    char name[20];//20
    int age;//4
    char* address;//一个字符占两个字节4*2=8
};

int main(){
    Teacher t;
    Teacher& t1=t;
    Teacher* p=&t;
    cout<

5.9、指针判空与别名判空

指针方式判空:编译通过,不能提前防止错误

别名方式判空:编译不通过,提前防止错误

struct Teacher{
    char name[20];//20
    int age;//4
};

void myprint(Teacher* t){
    cout<name<<","<age<

六、函数

6.1、函数默认参数、重载

void myprint(int x,int y=9,int z=8){
    cout<

6.2、可变参数

可变参数需要约定参数类型

void VariableArgumentMethod(int pArg,...){
    //声明一个指针, 用于持有可变参数
    va_list args_p;
    //将 pArg 初始化为指向第一个参数
    va_start(args_p, pArg);
    // 输出参数
    for (int i=0; i!=pArg; i++) {
        // 获取 pArg 所指向的参数并输出
        printf("%d, ", va_arg(args_p, int) );
    }
    //结束
    va_end(args_p);
}

int main(){
    VariableArgumentMethod(3,20,30,40);//20, 30, 40
    return 0;
}

6.3、C++类的普遍写法

实体类(头文件.h、执行文件.cpp)、main函数分离写法

//MyTeacher.h
class MyTeacher {
public:
    int age;
    char *name;
public:
    void setAge(int age);

    int getAge();

    void setName(char *name);

    char *getName();
};
//MyTeacher.cpp
#include 
#include "MyTeacher.h"

void MyTeacher::setAge(int age) {
    this->age = age;
}

int MyTeacher::getAge() {
    return this->age;
}

void MyTeacher::setName(char *name) {
    this->name = name;
}

char *MyTeacher::getName() {
    return this->name;
}
//main.cpp
#include 
#include "MyTeacher.h"
using namespace std;
int main() {
    MyTeacher t1;
    t1.setName("zhangsan");
    t1.setAge(18);

    cout<

6.4、构造函数

using namespace std;

class Teacher {
private:
    char *name;
    int age;
public:
    //无参构造函数(写了,就会覆盖默认的无参构造函数)
    Teacher() {
        cout << "无参构造函数" << endl;
    }

    Teacher(char *name, int age) {
        this->name = name;
        this->age = age;
        cout << "有参构造函数" << endl;
    }

    char *getName() {
        return name;
    }
};

int main() {
    Teacher t1;
    Teacher t2("Jsason", 20);

    //另外一种调用方式
    Teacher t3 = Teacher("Jack", 21);
    return 0;
}

6.5、析构函数

using namespace std;

class Teacher {
private:
    char *name;
public:
    //无参构造函数(写了,就会覆盖默认的无参构造函数)
    Teacher() {
        this->name=(char*)malloc(100);
        strcpy(name,"zhangsan");
        cout << "无参构造函数" << endl;
    }
    //析构函数
    //当对象要被系统释放时,析构函数被调用
    //作用:善后处理
    ~Teacher(){
        cout<<"析构"<name);
    }
};

int main() {
    Teacher t1;
    return 0;
}
//无参构造函数
//析构

6.6、拷贝构造函数

class Teacher{
private:
    char* name;
    int age;
public:
    Teacher(char* name,int age){
        this->name=name;
        this->age=age;
        cout<<"有参构造函数"<name=obj.name;
        this->age=obj.age;
        cout<<"拷贝构造函数"<

6.6.1、拷贝构造函数被调用的场景

声明时赋值

int main(){
    Teacher t1("rose",20);
    Teacher t2=t1;
    return 0;
}
//有参构造函数
//拷贝构造函数

作为参数传入,实参给形参赋值

void func1(Teacher t){
    t.myprint();
}

int main(){
    Teacher t1("rose",20);
    func1(t1);
    return 0;
}
//有参构造函数
//拷贝构造函数
//rose,20

作为函数返回值返回,给变量初始化赋值

第一次拷贝构造:t1赋值给形参赋值

第二次拷贝构造:func1返回t对象

Teacher func1(Teacher t){
    t.myprint();
    return t;
}
int main(){
    Teacher t1("rose",20);
    Teacher t3 = func1(t1);
    return 0;
}
//有参构造函数
//拷贝构造函数
//rose,20
//拷贝构造函数

6.6.2、浅拷贝(值拷贝)问题

浅拷贝也就是赋值拷贝,编译器默认实现浅拷贝。

下面代码name字段为堆内存地址,直接赋值实现的是浅拷贝。

class Teacher{
private:
    char* name;
    int age;
public:
    Teacher(char* name,int age){
        this->name=(char*)malloc(100);
        strcpy(this->name,name);
        this->age=age;
        cout<<"有参构造函数"<name);
        this->name = NULL;
    }
    //浅拷贝
//    Teacher(const Teacher& obj) {
//        this->name = obj.name;
//        this->age = obj.age;
//    }
    void myprint(){
        cout<

深拷贝。对name字段重新开辟空间赋值

class Teacher{
private:
    char* name;
    int age;
public:
    Teacher(char* name,int age){
        this->name=(char*)malloc(100);
        strcpy(this->name,name);
        this->age=age;
        cout<<"有参构造函数"<name);
        this->name = NULL;
    }
    //深拷贝
    Teacher(const Teacher& obj) {
        //复制name属性
        int len = strlen(obj.name);
        this->name = (char*)malloc(len+1);
        strcpy(this->name, obj.name);
        this->age = obj.age;
    }
    void myprint(){
        cout<

6.6.3、构造函数的属性初始化列表

这里c++初始化属性对象使用这个极大方便了初始化

Student(int id,char t1_n,char t2_n):t1(t1_n),t2(t2_n)

class Teacher{
private:
    char* name;
public:
    Teacher(char* name){
        this->name=name;
        cout<<"Teacher有参构造函数"<name;
    }
};

class Student{
private:
    int id;
    //属性对象
    Teacher t1;
    Teacher t2;
public:
    Student(int id,char *t1_n,char* t2_n):t1(t1_n),t2(t2_n){
        this->id=id;
        cout<<"Student有参构造函数"<

6.7、C++ 通过new(delete)动态内存分配

类的创建与释放

class Teacher{
private:
    char* name;
public:
    Teacher(char* name){
        this->name=name;
        cout<<"Teacher有参构造函数"<name=name;
    }
    char* getName(){
        return this->name;
    }
};

int main(){
    //C++ 动态内存分配
    //会调用构造和析构函数
    Teacher *t1=new Teacher("Jack");
    cout<getName()<setName("Rose");
    cout<getName()<

数组的创建与释放

int main(){
    //数组类型
    //C
    int *p1=(int*)malloc(sizeof(int)*10);
    p1[0]=9;
    free(p1);
    
    //C++
    int *p2=new int[10];
    p2[0]=9;
    delete p2;
    return 0;
}

6.8、静态属性和方法

class Teacher{
public:
    char* name;
    //计数器
    static int total;
public:
    Teacher(char* name){
        this->name=name;
        cout<<"Teacher有参构造函数"<name=name;
    }
    char* getName(){
        return this->name;
    }
    //计数,静态函数
    static void count(){
        total++;
        cout<

6.9、类的大小

C/C++ 内存分区:栈、堆、全局(静态、全局)、常量区(字符串)、程序代码区

普通属性与结构体相同的内存布局

这里类A、类B不计算静态变量、方法的大小

class A{
public:
    int i;
    int j;
    int k;
    static int m;
};
class B{
public:
    int i;
    int j;
    int k;
    void myprint(){
        cout<<"打印"<

6.10、常函数

常函数中只有只读操作

class Teacher{
private:
    char* name;
    int age;
public:
    Teacher(char* name,int age){
        this->name=name;
        this->age=age;
        cout<<"Teacher有参构造函数"<name="Jack";
        //不允许,改变this指针的值
        //this = (Teacher*)0x00009;
        cout << this->name << "," << this->age << endl;
    }
    void myprint2(){
        cout << this->name << "," << this->age << endl;
    }
};

int main(){
    Teacher t1("Jack",20);
    //常量对象只能调用常量函数,不能调用非常量函数
    const Teacher t2("Rose",18);
    //常函数,当前对象不能被修改,防止数据成员被非法访问
    //t2.myprint2();
    return 0;
}

6.11、友元函数

友元函数的实现,在友元函数中可以访问私有的属性

java中的反射访问私有属性,底层实现也是友元函数

class A{
    //友元函数
    friend void modify_i(A* p,int a);
private:
    int i;
public:
    A(int i){
        this->i=i;
    }
    void myprint(){
        cout<i=a;
}

int main(){
    A* a=new A(10);
    a->myprint();//10
    
    modify_i(a, 20);
    a->myprint();//20
    return 0;
}

6.12、友元类

class A{
    //友元类
    friend class B;
private:
    int i;
public:
    A(int i){
        this->i=i;
    }
    void myprint(){
        cout<

6.13、运算符重载

class Point{
public:
    int x;
    int y;
public:
    Point(int x=0,int y=0){
        this->x=x;
        this->y=y;
    }
    void myprint(){
        cout<

或者写在成员函数中

class Point{
public:
    int x;
    int y;
public:
    Point(int x=0,int y=0){
        this->x=x;
        this->y=y;
    }
    //成员函数,运算符重载
    Point operator+(Point& p1){
        Point tmp(this->x+p1.x,this->y+p1.y);
        return tmp;
    }
    void myprint(){
        cout<

当属性私有时,通过友元函数完成运算符重载

class Point{
    friend Point operator+(Point& p1,Point& p2);
    friend Point operator-(Point& p1,Point& p2);
private:
    int x;
    int y;
public:
    Point(int x=0,int y=0){
        this->x=x;
        this->y=y;
    }
    void myprint(){
        cout<

七、面向对象OOP

7.1、继承

#include 
using namespace std;

class Human{
public:
    void say(){
        cout<<"说话"<

7.2、向父类构造方法传参

class Human{
public:
    Human(char* name,int age){
        this->name=name;
        this->age=age;
    }
    void say(){
        cout<<"说话"<brother=brother;
    }
    void chasing(){
        cout<<"泡妞"<

7.3、构造函数与析构函数调用的顺序

#include 
using namespace std;

class Human{
public:
    Human(char* name,int age){
        this->name=name;
        this->age=age;
        cout<<"Human 构造函数"<brother=brother;
        cout<<"Man 构造函数"<

7.4、子类对象调用父类的成员

m1.Human::say();

#include 
using namespace std;

class Human{
public:
    Human(char* name,int age){
        this->name=name;
        this->age=age;
    }
    
    void say(){
        cout<<"说话"<brother=brother;
    }
    
    void chasing(){
        cout<<"泡妞"<

7.5、多继承

//人
class Person{
    
};
//公民
class Citizen{
    
};
//学生,既是人,又是公民
class Student:public Person,public Citizen{
    
};
//继承的访问修饰
//基类中      继承方式             子类中
//public     & public继承 = > public
//public     & protected继承 = > protected
//public     & private继承 = > private
//
//protected  & public继承 = > protected
//protected  & protected继承 = > protected
//protected  & private继承 = > private
//
//private    & public继承 = > 子类无权访问
//private    & protected继承 = > 子类无权访问
//private    & private继承 = > 子类无权访问

7.6、继承的二义性

继承的二义性:多继承中对父类都存在的重复属性赋值会在编译中报错。

1,二义性可以指定父类使用如b.A1::name

2,打印时继承的二义性默认使用最后一个父类的属性

3,多继承可以使用virtual虚继承处理二义性问题

class A{
public:
    char* name;
};

class A1:virtual public A{
public:
    A1(){
        this->name="A1";
    }
};

class A2:virtual public A{
public:
    A2(){
        this->name="A2";
    }
};
class B:public A1,public A2{
    
};
int main(){
    B b;
    //    b.name="Jack";
    cout<

7.7、虚函数

虚函数运用在多态上,可以提高程序的拓展性。

多态分为:

1,动态多态:程序运行过程中,觉得哪一个函数被调用(重写)

2,静态多态:重载

java的多态条件

1、继承

2、重写父类方法

3、父类引用持有子类对象

以下示例通过动态多态体现了虚函数

//main函数
#include "Plane.h"
#include "Jet.h"
#include "Copter.h"

void bizPlay(Plane &p){
    p.fly();
    p.land();
}

int main(){
    Copter p2;
    bizPlay(p2);

    Jet p3;
    bizPlay(p3);
    return 0;
}
//直升飞机在原地起飞...
//直升飞机降落在女神的屋顶...
//喷气式飞机在跑道上起飞...
//喷气式飞机在跑道上降落...

普通飞机类

//Plane.h文件
//普通飞机
class Plane{
public:
    virtual void fly();
    virtual void land();
};

//Plane.cpp文件
#include "Plane.h"
#include 
using namespace std;

void Plane::fly(){
    cout<<"起飞"<

直升飞机类

//Copter.h文件
#pragma once
#include 
#include "Plane.h"
class Copter:public Plane{
public:
    virtual void fly();
    virtual void land();
};

//Copter.cpp文件
#include "Copter.h"
#include 
using namespace std;

void Copter::fly() {
    cout << "直升飞机在原地起飞..." << endl;
}

void Copter::land() {
    cout << "直升飞机降落在女神的屋顶..." << endl;
}

喷气式飞机类

//Jet.h文件
#include "Plane.h"
//喷气式飞机
class Jet:public Plane{
public:
    virtual void fly();
    virtual void land();
};

//Jet.cpp文件
void Jet::fly(){
    cout << "喷气式飞机在跑道上起飞..." << endl;
}

void Jet::land() {
    cout << "喷气式飞机在跑道上降落..." << endl;
}

7.8、纯虚函数

纯虚函数(抽象类)
1.当一个类具有一个纯虚函数,这个类就是抽象类
2.抽象类不能实例化对象
3.子类继承抽象类,必须要实现纯虚函数,如果没有,子类也是抽象类
抽象类的作用:为了继承约束,根本不知道未来的实现

class Shape{
public:
    virtual void sayArea()=0;
    void print(){
        cout<<"hi"<r=r;
    }
    void sayArea(){
        cout<<"圆的面积"<<(3.14*r*r)<
//接口(只是逻辑上的划分,语法上跟抽象类的写法没有区别)
//可以当做一个接口
class Drawable{
    virtual void draw();
};

7.9、模板函数(泛型)

void myswap(int &a,int &b){
    int temp=a;
    a=b;
    b=temp;
}

void myswap(char &a,char &b){
    char temp=a;
    a=b;
    b=temp;
}

int main(){
    int a=10,b=20;
    myswap(a, b);
    
    char c='c',d='d';
    myswap(c, d);
    return 0;
}

发现:这两个函数业务逻辑一样,数据类型不一样

template 
void myswap(T& a,T& b){
    T temp=a;
    a=b;
    b=temp;
}

7.10、模板类

类似于java的List list;

template
class A{
public:
    A(T a){
        this->a=a;
    }
protected:
    T a;
};

//普通类继承模板类
class B:public A{
    B(int a,int b):A(a){
        this->b=b;
    }
private:
    int b;
};

//模板类继承模板类
template 
class C:public A{
public:
    C(TT c,TT a):A(a){
        this->c=c;
    }
protected:
    TT c;
};

int main(){
    //实例化模板类对象
    //类似于java的List list;
    A a(6);
    return 0;
}

7.11、类的继承与内存布局

7.11.1、类为空

类为空情况下:

在C++中分配体系中什么没有sizeof为1

在C中什么都没有默认分配0

class Base {
};

int main()
{
    cout << "Base Size:"<< sizeof(Base) <

7.11.2、类里有一个虚函数

类里有一个虚函数情况下:

这里8+4=12,字节对齐为16

字节对齐(内存对齐):结构体变量的大小,必须是最宽基本数据类型的整数倍。

class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    int base;
};

int main()
{
    cout << "Base Size:"<< sizeof(Base) <

7.11.3、类里有多个虚函数

类里有多个虚函数情况下:

在C++中用虚函数表存放虚函数,这里只有一个虚函数指针,因此仍然为8+4=12,字节对齐为16

class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    int base;
};

int main()
{
    cout << "Base Size:"<< sizeof(Base) <

7.11.4、有虚函数继承无重载

有虚函数继承无重载情况下:

这里子类Child1会拷贝一份Base的虚函数表到子类虚函数表中,因此8+4+4=16,字节对齐后仍然是16

C++语言核心知识梳理_第1张图片
class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    int base;
};

//子类1,无虚函数重载
class Child1 : public Base
{
public:
    virtual void f1() { cout << "Child1::f1" << endl; }
    virtual void g1() { cout << "Child1::g1" << endl; }
    virtual void h1() { cout << "Child1::h1" << endl; }
    int child1;
};

int main()
{
    cout << "Child1 Size:"<< sizeof(Child1) <

7.11.5、有一个虚函数重载

有一个虚函数重载情况下子类虚函数表会优先存放自己的虚函数,因此这里的虚函数f会覆盖父类的。sizeof大小仍然是8+4+4=16

C++语言核心知识梳理_第2张图片
class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    int base;
};

//子类2,有一个虚函数重载
class Child2 : public Base {
public:
    virtual void f() { cout << "Child2::f" << endl; }
    virtual void g1() { cout << "Child2::g1" << endl; }
    virtual void h1() { cout << "Child2::h1" << endl; }
    int child2;
};

int main() {
    cout << "Child2 Size:" << sizeof(Child2) << endl;//Child2 Size:16
}

7.11.6、全部虚函数重载

全部虚函数重载情况下回完全覆盖父类的虚函数表

C++语言核心知识梳理_第3张图片
class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    int base;
};

//子类3,全部虚函数重载
class Child3 : public Base
{
public:
    virtual void f() { cout << "Child3::f" << endl; }
    virtual void g() { cout << "Child3::g" << endl; }
    virtual void h() { cout << "Child3::h" << endl; }
protected:
    int x;
};

int main()
{
    cout << "Child3 Size:"<< sizeof(Child3) <

7.11.7、多继承

多继承情况下,第一个继承类的虚函数会拷贝到子类的虚函数表中,第二个的继承类会把指针给拷贝过来多生成一个虚函数表。也就是说有几个继承就有几个虚表

因此这里的sizeof为两个虚函数表8,三个int变量4,8+8+4+4+4,字节对齐为24

C++语言核心知识梳理_第4张图片
class Base3 {
public:
    virtual void f3() { cout << "Base3::f3" << endl; }
    virtual void g3() { cout << "Base3::g3" << endl; }
    virtual void h3() { cout << "Base3::h3" << endl; }
    int base3;
protected:
private:
};

class Base4 {
public:
    virtual void f4() { cout << "Base4::f4" << endl; }
    virtual void f3() { cout << "Base4::f4" << endl; }
    virtual void g4() { cout << "Base4::g4" << endl; }
    virtual void h4() { cout << "Base4::h4" << endl; }
    int base4;
protected:
private:
};

class Child5 : public Base3, public Base4 {
public:
    virtual void f3() { cout << "child5::f3" << endl; }
    virtual void f4() { cout << "Child5::f4" << endl; }
    virtual void f5() { cout << "Child5::f5" << endl; }
    virtual void g5() { cout << "Child5::g5" << endl; }
    virtual void h5() { cout << "Child5::h5" << endl; }
    int child5;
protected:
private:
};

int main() {
    cout << "Child5 Size:" << sizeof(Child5) << endl;//Child5 Size:32
}

7.11.8、菱形继承

棱形继承在多继承的基础上添加了父类,其原理都一样。这里sizeof为(8+4+4)+(8+4+4+4)字节对齐为40

C++语言核心知识梳理_第5张图片
class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    int base;
};

//多重继承体系下的
class Base3 : public Base {
public:
    virtual void f3() { cout << "Base3::f3" << endl; }

    virtual void g3() { cout << "Base3::g3" << endl; }

    virtual void h3() { cout << "Base3::h3" << endl; }

    int base3;
};

class Base4 : public Base {
public:
    virtual void f4() { cout << "Base4::f4" << endl; }

    virtual void f3() { cout << "Base4::f4" << endl; }

    virtual void g4() { cout << "Base4::g4" << endl; }

    virtual void h4() { cout << "Base4::h4" << endl; }

    int base4;
};

class Child6 : public Base3, public Base4 {
public:
    int child5;
};

int main() {
    cout << "Child6 Size:" << sizeof(Child6) << endl;//Child6 Size:40
}

7.12、虚继承的内存布局

虚拟继承的子类的内存结构,和普通继承完全不同。虚拟继承的子类,有单独的虚函数表,另外也单独保存一份父类的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界。子类的内存中,首先是自己的虚函数表,然后是子类的数据成员,然后是0x0,之后就是父类的虚函数表,之后是父类的数据成员。

如果子类没有自己的虚函数,那么子类就不会有虚函数表,但是子类数据和父类数据之间,还是需要0x0来间隔。因此,在虚拟继承中,子类和父类的数据,是完全间隔的,先存放子类自己的虚函数表和数据,中间以0x分界,最后保存父类的虚函数和数据。如果子类重载了父类的虚函数,那么则将子类内存中父类虚函数表的相应函数替换

7.12.1、单虚继承

如下面虚继承示例中,sizeof便为(8+4)+(8+4)内存对齐也就是16+16=32

C++语言核心知识梳理_第6张图片
class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    int base;
};


class Child : virtual public Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void f1() { cout << "Child::f1" << endl; }
    virtual void g1() { cout << "Child::g1" << endl; }
    virtual void h1() { cout << "Child::h1" << endl; }
    int child;
};

int main() {
    cout << "Child6 Size:" << sizeof(Child) << endl;//Child6 Size:32
    return 0;
}

7.12.2、菱形虚继承

1.当前类有虚, 则开辟虚表,虚表指针给入当前对象指针

2.多继承下,开辟虚表根据继承的类走,会有多个指针

下面示例Child7的结构为:Base3、Base4虚继承于Base,然后Child7多继承于Base3、Base4,Child7的虚表会和Base3合并。

因此sizeof则为:(8+4)+(8+4)+(4+8+4)字节对齐后为16+16+16=48

C++语言核心知识梳理_第7张图片
class Base
{
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    int base;
protected:
private:
};


class Base3 :virtual public Base
{
public:
    virtual void f3() { cout << "Base3::f3" << endl; }
    virtual void g3() { cout << "Base3::g3" << endl; }
    virtual void h3() { cout << "Base3::h3" << endl; }
    int base3;
protected:
private:
};

class Base4 :virtual public Base
{
public:
    virtual void f4() { cout << "Base4::f4" << endl; }
    virtual void g4() { cout << "Base4::g4" << endl; }
    virtual void h4() { cout << "Base4::h4" << endl; }
    int base4;
protected:
private:
};

class Child7 : public Base3, public Base4
{
public:
    virtual void f()  { cout << "child7::f" << endl; }
    virtual void f3() { cout << "child7::f3" << endl; }
    virtual void f4() { cout << "Child7::f4" << endl; }
    virtual void f7() { cout << "Child7::f7" << endl; }
    virtual void g7() { cout << "Child7::g7" << endl; }
    virtual void h7() { cout << "Child7::h7" << endl; }
    int child7;
protected:
private:
};
int main()
{
    cout << "Child7 Size:" << sizeof(Child7) << endl;//Child7 Size:48
    return 0;
}

7.13、类的内存开辟是堆还是栈

Linux下32位环境的用户空间内存分布情况

C++语言核心知识梳理_第8张图片

其中栈内存是递减开辟,堆内存是递增开辟。

那么我们依次开辟class book对象,发现内存地址是递减的,说明类的内存开辟在栈上

class book {
};

int main() {
    book b1 = *new book();
    book b2 = *new book();
    book b3 = *new book();
    book b4 = *new book();
    cout << &b1 << "   |   " << &b2 << "   |   " << &b3 << "   |   " << &b4 << "   |   " << endl;
    return 0;
}
//0x7ff7ba3a2ff8   |   0x7ff7ba3a2ff0   |   0x7ff7ba3a2fe8   |   0x7ff7ba3a2fe0   |   

八、异常处理

8.1、throw基本数据

int main(){
    try {
            int age = 50;
            if (age > 200){
                throw 9.8;
            }
            else if (age < 100) {
                char *a="抛一个字符串";
                throw a;
            }
        }
        catch (int a) {
            cout << "int 异常" << a << endl;
        }
        catch (char* b) {
            cout << b << endl;
        }
        catch (...) {
            cout << "未知异常" << endl;
        }
    return 0;
}

8.2、throw 抛出函数外

void mydiv(int a,int b){
    if (b==0) {
        char *a="除数为0";
        throw a;
    }
}
void func(){
    try {
        mydiv(0, 0);
    } catch (char *a) {
        throw a;
    }
}
int main(){
    try {
        func();
    } catch (char* a) {
        cout<

8.3、抛出对象

抛出对象的引用【最合适】。

引用是对象的别名

class MyException{};
void mydiv(int a,int b){
    if (b==0) {
        throw MyException();
    }
}
int main(){
    try {
        mydiv(8, 0);
    }
    catch (MyException& e) {
        cout<<"MyException引用"<

抛出一个新对象,会产生对象副本

class MyException{};
void mydiv(int a,int b){
    if (b==0) {
        throw MyException();
    }
}
int main(){
    try {
        mydiv(8, 0);
    }
    //会产生对象副本
    catch(MyException e){
        cout<<"MyException"<

抛出指针

class MyException{};
void mydiv(int a,int b){
    if (b==0) {
        MyException* e1;
        throw e1;
    }
}
int main(){
    try {
        mydiv(8, 0);
    }
    catch(MyException* e1){
        cout << "MyException指针" << endl;
//        delete e1;
    }
    return 0;
}

8.4、throw 声明函数会抛出的异常类型

void mydiv(int a, int b) throw (char*, int) {
	if (b == 0) {
		throw "除数为零";
	}
}

8.5、标准异常

out_of_range(“超出范围”)

invalid_argument(“参数不合法”)

throw exception()

#include 

class NullPointException
{
private:
    char* message;
    
public:
    NullPointException(char* str = "A problem") : message{str} {}
    string_view what() const { return message; }
};

void mydiv(int a,int b){
    if (b>10) {
        throw out_of_range("超出范围");
    }
    else if(b==NULL){
        throw NullPointException("为空");
    }
    else if(b==0){
        throw invalid_argument("参数不合法");
    }else{
        throw exception();
    }
}

int main(){
    try {
        mydiv(1, 1);
    } catch (NullPointException &e2) {
        cout<

8.6、外部类异常

class Err{
public:
    class MyException{
    public:MyException(){
        
    }
    };
};
void mydiv(int a,int b){
    if (b>10) {
        throw Err::MyException();
    }
}

九、类型转换

static_cast 普遍情况
const_cast 去常量
dynamic_cast 子类类型转为父类类型
reinterpret_cast 函数指针转型,不具备移植性

9.1、static_cast 普遍情况

//C
char *c_p2 = (char *) func(2);
//C++意图明显
char *c_p = static_cast (func(2));

示例:

void *func(int type) {
    switch (type) {
        case 1: {
            int i = 9;
            return &i;
        }
        case 2: {
            char a = 'X';
            return &a;
        }
        default:
            return NULL;
    }
}

int main() {
    int i = 0;
    //原始类型转换(自动转换)
  	//原始类型转换,所有情况都是一种写法,可读性不高,有可能有潜在的风险
    double d = i;
//    double d = (double) i;
    cout << d << endl;//0

    //C
    char *c_p2 = (char *) func(2);
    cout << *c_p2 << endl;//X

    //C++意图明显
    char *c_p = static_cast (func(2));
    cout << *c_p << endl;//X

    int j = 0;
    d = 9.5;
    j = static_cast(d);
    cout << j << endl;//9
    return 0;
}

9.2、const_cast 去常量

//c
char *c_p1 = (char *) c;
//c++
char *c_p = const_cast(c);

去常量可以编译const常量字符数组,示例:

void func(const char c[]) {
//    c[1] = 'a';//常量数组不可修改

    //c,其他人并不知道,这次转型是为了去常量
    char *c_p1 = (char *) c;
    c_p1[1] = 'X';
    cout << c << endl;//hXllo

    //c++
    char *c_p = const_cast(c);
    c_p[1] = 'Y';
    cout << c << endl;//hYllo
}

int main() {
    char c[] = "hello";
    func(c);
    return 0;
}

9.3、dynamic_cast 子类类型转为父类类型

Man *m = (Man *) obj;
Man *m1 = dynamic_cast(obj);

Women、Man向上转型为Person会成功

Person向下转型为Man,根据原始类型可能成功,可能失败

class Person {
public:
    virtual void print() {
        cout << "人" << endl;
    }
};

class Man : public Person {
public:
    void print() {
        cout << "男人" << endl;
    }

    void chasing(char *str) {
        cout << str << "泡妞" << endl;
    }
};

class Women : public Person {
public:
    void print() {
        cout << "女人" << endl;
    }

    void carebaby() {
        cout << "生孩子" << endl;
    }
};

void func(Person *obj) {
    //调用子类的特有的函数,转为实际类型
    //并不知道转型失败
    Man *m = (Man *) obj;
    m->chasing("c:");

    //转型失败,返回NULL
    Man *m1 = dynamic_cast(obj);
    if (m1 != NULL) {
        m1->chasing("c++:");
    }
}

int main() {
    Women w1;
    Person *p1 = &w1;
    func(p1);//c:泡妞(按理说这里都会转型失败,问题待排查)

    Man m1;
    Person *p2 = &m1;
    func(p2);//c:泡妞、c++:泡妞

    return 0;
}

9.4、reinterpret_cast 函数指针转型

//C方式
f_array[2] = (f_p) func2;
//C++ 方式
f_array[1] = reinterpret_cast(func2);

这里typedef void(*f_p)();函数指针无非保存的是一个地址,与void等类型没有关系

注意:reinterpret_cast这个操作符的转换几乎总是与编译平台息息相关,所以reinterpret_cast不具备移植性!

某些情况下可能会导致不正确的结果,应该尽量避免函数指针转型

void func1() {
    cout << "func1" << endl;
}

char *func2() {
    cout << "func2" << endl;
    return "abc";
}

typedef void(*f_p)();

int main() {
    //函数指针数组
    f_p f_array[6];
    //赋值
    f_array[0] = func1;

    //C方式
    f_array[2] = (f_p) func2;
    f_array[2]();//func2

    //C++ 方式
    f_array[1] = reinterpret_cast(func2);
    f_array[1]();//func2
    return 0;
}

十、IO流

10.1、文本文件操作

ofstream:是outOfStream的意思

ifstream:是inOfStream的意思

下面示例会把“jack"\n"rose"写入到指定文本文件

int main() {
    char *fname = "/Users/zhanglei/Desktop/text.txt";
    //输出流
    ofstream fout(fname);
    //创建失败
    if (fout.bad()) {
        return 0;
    }
    fout << "jack" << endl;
    fout << "rose" << endl;

    //关闭
    fout.close();

    //读取
    ifstream fin(fname);
    if (fin.bad()) {
        return 0;
    }
    char ch;
    while (fin.get(ch)) {
        //输出
        cout << ch;
    }
    fin.close();
    return 0;
}

10.2、二进制文件

示例会拷贝指定文件

int main() {
    char *src = "/Users/zhanglei/Desktop/React Native eslint配置讨论.html";
    char *dest = "/Users/zhanglei/Desktop/React Native eslint配置讨论_dest.html";

    //输入流
    ifstream fin(src, ios::binary);
    //输出流
    ofstream fout(dest, ios::binary);

    if (fin.bad() || fout.bad()) {
        return 0;
    }
    while (!fin.eof()) {
        //读取
        char buff[1024] = {0};
        fin.read(buff, 1024);
        //写入
        fout.write(buff, 1024);
    }
    //关闭
    fin.close();
    fout.close();
    return 0;
}

10.3、C++对象的持久化

p1写成(char *) (&p1)是因为这里需要传入指针

下面实例实现了C++对象持久化的储存、读取

class Person {
public:
    Person() {

    }

    Person(char *name, int age) {
        this->name = name;
        this->age = age;
    }

    void print() {
        cout << name << "," << age << endl;
    }

private:
    char *name;
    int age;
};

int main() {
    Person p1("柳岩", 25);
    Person p2("rose", 18);
    //输出流
    ofstream fout("/Users/zhanglei/Desktop/p_obj.data", ios::binary);
    //指针能够读取到正确的数据,读取内存区的长度
    fout.write((char *) (&p1), sizeof(Person));
    fout.write((char *) (&p2), sizeof(Person));
    fout.close();

    //输入流
    ifstream fin("/Users/zhanglei/Desktop/p_obj.data", ios::binary);
    Person tmp;
    fin.read((char *) (&tmp), sizeof(Person));
    tmp.print();

    fin.read((char *) (&tmp), sizeof(Person));
    tmp.print();
    return 0;
}

十一、标准模板库

stl :standard template library 。

11.1、string字符串

string字符串初始化方式、拼接、转c字符串、string常用api at

#include 
int main() {
    //初始化方式
    string s1 = "I love ";
    string s2("China");
    //拼接
    string s3 = s1 + s2;
    cout << s3 << endl;//I love China
    //转c字符串
    const char *c_str = s3.c_str();
    cout << c_str << endl;//I love China
    cout << s1.at(2) << endl;//l
    return 0;
}

11.2、vector容器

#include 

int main() {
    //动态数据
    //不需要使用动态内存分配,就可以使用动态数组
    vector v;
    v.push_back(12);
    v.push_back(10);
    v.push_back(5);
    for (int i = 0; i < v.size(); ++i) {
        cout << v[i] << endl;
    }
    return 0;
}
//12
//10
//5

你可能感兴趣的:(#,c,c++,c++,开发语言,数据结构)