C++基础

避免头文件重复定义,

#pragma once
//或者头文件有一个自动生成的宏
__头文件的宏_H
extern C的用法:
#ifdef __cplusplus
extern "C" {
int sum(int v1, int v2);
int delta(int v1, int v2);
int divide(int v1, int v2);
}
#endif // __cplusplus
内联函数: inline function ,关键词: inline ,使用的时候编译器会直接将代码粘贴

什么时候使用:

  1. 调用频繁
  2. 函数代码少
关键词:const
int age = 10;
int height = 30;
int score = 99;
//p1 指向可以改变, *p1不能改变
const int *p1 = &age;           //(可以 p1 = &score; 不能 *p1 = score );
//p2 指向可以改变, *p2不行
int const *p2 = &age;           //(同上)
//p3 指向不能改变 *p3 可以改变
int * const p3 = &age;          //(可以 *p3 = score; 不能 p3 = &score );

// p4是常量,*p4也是常量
const int * const p4 = &age;
// p5是常量,*p5也是常量
int const * const p5 = &age;

引用:类似于指针,但不能更改,定义时必须初始化,
引用存在的价值: 比指针更安全、函数返回值可以被赋值

//注意,这里是&v1 所以可以直接传a和b,如果是指针 int* 则需要 swap(&a,&b)
void swap(int& v1, int& v2) {
    int tmp = v1;
    v1 = v2;
    v2 = tmp;
}

swap(a, b);
cout << "a = " << a << ", b = " << b << endl;

常引用:引用被const修饰,这样就无法通过引用修改数据。
const引用的特点:
1、可以指向临时数据(常量、表达式、函数返回值等)
2、可以指向不同类型的数据
3、作为函数参数时
✓ 可以接受const和非const实参(非const引用,只能接受非const实参)
✓ 可以跟非const引用构成重载
4、 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量

数组的引用:常见的两种写法

int array[] = {10,20,30}
int (&ref1)[3] = array;
int * const &ref2 = array;

类,对象的内存布局
struct Person{
    int m_id;
    int m_age;
    int m_height;
    void display() {//可以用this,但不能this.m_age,因为this是指针
        cout << "m_id is " << this->m_id << endl;
        cout << "m_age is" << m_age << endl;
        cout << "m_height is" << m_height << endl;
    }   
};

Person person;                  //0x00E69B60
person.m_id = 10;               //0x00E69B60
person.m_age = 20;              //0x00E69B64
person.m_height = 30;           //0x00E69B68

Person* p = (Person*)&person.m_age; //0x00E69B64
p->m_id = 40;       //不偏移地址直接赋值  0x00E69B64 = 40  即person.m_age = 40
p->m_age = 50;      //往后偏移4个地址  0x00E69B68 = 50; person.m_height = 50
//指针访问对象成员的本质
person.display();     //m_id is 10  m_age is40  m_height is50
p->display();       //m_id is 40 m_age is50 m_height is-858993460

封装: 成员变量私有化,提供公共的getter和setter给外界去访问成员变量

struct Student{
private: 
    int age;
public: 
    void setAge(int age) {
        this->age = age;
    }
    int getAge() {
        return this->age;
    }
};
内存布局:

代码段: 用于存放代码
数据段: 用于存放全局变量等
栈空间:没调用一个函数就会给它分配一段连续的栈空间,等待函数调用完毕后会自动回收,自动分配和回收
堆空间: 需要主动去申请和释放 , 为了能够自由控制内存的生命周期、大小,会经常使用堆空间的内存

堆空间的申请与释放
// malloc  free
int *p = (int *) malloc(4);
*p = 10;
free(p);

//new  delete 
char *p = new int;
*p = 10;
delete p;

//new[]  delete[] 
char *p = new char[4];
delete[] p;

int *p1 = new int;   //空间未初始化
int *p2 = new int();//空间初始化为0
int *p3 = new int(5);//空间初始化为5
int *p4 = new int[3];//空间未被初始化
int *p5 = new int[3]();//3个元素都被初始化为0
int *p6 = new int[3]{};//3个元素都被初始化为0
int *p7 = new int[3]{5};//首元素初始化为5,其他元素初始化为0

memset函数是将较大的数据结构(比如对象、数组等)内存清零的比较快的方法

Person person;
memset(&person, 0, sizeof(person));

Person persons[] = { { 1, 20, 180 }, { 2, 25, 165}, { 3, 27, 170 } };
memset(persons, 0, sizeof(persons));

内存空间:

//全局区
Person g_person;
int main() {
    //栈空间
    Person person;
    //堆空间,没有初始化成员变量
    Person* p1 = new Person;
    //堆空间,初始化成员变量为0
    Person* p2 = new Person;
    return 0;
}
构造函数,析构函数

对象初始化还可以在构造函数中添加:

Person(){
    memset(this, 0, sizeof(Person));
}

注意 :
通过malloc分配的对象free的时候不会调用析构函数
构造函数、析构函数要声明为public,才能被外界正常使用

struct Car{
    int m_price;
};

struct Person{
    int m_age;
    Car m_car;
    Car* my_car;
};
my_car 需要自己申请空间,自己回收.
m_car 对象创建就有,与对象内存空间关联

类的声明和实现:
h文件
#pragma once
namespace Sean { //自定义命名空间
    class Person{
    private:
        int m_age;
    public:
        void setAge(int age);
        int getAge();
    };
}
cpp文件
#include "Person.h"
namespace Sean{
    void Person::setAge(int age) {
        m_age = age;
    }
    int Person::getAge() {
        return m_age;
    }
}
命名空间 namespace 避免命名冲突
Sean::Person* p = new Sean::Person();
using namespace Sean;  //优先使用此命名空间
//这样就可以直接像下面这样写
Person* p = new Person();
初始化列表:
//注意,这个需要在.h里面生命
Person::Person(int age, int height) :m_age(age), m_height(height) {
}
Person::Person(int age, int height) :m_height(height), m_age(m_height) {
}
m_age 和 m_height分别是多少
Person p(20,180);
m_age未知,m_height 为180
构造函数互相调用:
Person::Person(){
    new(this) Person(10, 20);
}
虚函数:关键字: virtual

只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(也就是说子类中可以省略virtual关键字 )

虚表:虚函数的实现原理是虚表,虚表里面存储着最终调用函数的地址,这个虚表也叫虚函数表

class Animal {
public:
    int m_age;
    //纯虚函数 类似OC的协议接口,只有声明,未实现
    virtual void speak() = 0;
    virtual void run() = 0;
};

class Cat : public Animal{
public:
    int m_life;
    void speak() {
        //调用父类的成员函数
        Animal::speak();
        cout << "Cat::speak()" << endl;
    }
    void run() {
        cout << "Cat::run()" << endl;
    }
};

所有的Cat对象(不管在全局区、栈、堆)共用同一份虚表

Animal* cat = new Cat();
cat->m_age = 20;
cat->speak();
cat->run();

在内存中的形式:
image.png

如果存在父类指针指向子类对象的情况,应该将析构函数声明为虚函数(虚析构函数)
delete父类指针时,才会调用子类的析构函数,保证析构的完整性

多继承

初始化方法:

class Student{
    int m_score;
public:
    void eat(){}
    Student(int score) : m_score(score) {
    };
};
class Worker{
    int m_salary;
public:
    void eat(){}
    Worker(int m_salary) : m_salary(m_salary) {
    };
};
class Undergraduate: public Student, public Worker{
public:
    Undergraduate(int score, int m_salary) : Student(score), Worker(m_salary) {
    };
};

多继承-虚函数

如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表

image.png

同名函数,同名变量

Undergraduate ug;
ug.Student::eat();
ug.Worker::eat();

ug.Student::m_age;
ug.Worker::m_age;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SeanLink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值