• C++基础


     变量存在的意义:方便管理内存。

    变量创建的语法:数据类型 变量名=变量的初始值;

             int   a = 10;

    #define 宏常量

      通常在文件上方定义,表示一个常量

    #const修饰的变量const 数据类型 变量名 = 常量值;

    通常在变量定义前面加关键字const,修饰该变量伟常量,不可修改。

    区别:define是宏定义,程序在预处理阶段将用define定义的内容进行了替换。因此程序运行时,常量表中并没有用define定义的常量,系统不为它分配内存。const定义的常量,在程序运行时在常量表中,系统为它分配内存。

    C++ 关键字

    标识符命名规则

    作用:C++规定给标识符(变量,常量)命名时,有自己的规则。

    1、标识符不能是关键字

    2、标识符只能由字母、数字、下划线组成

    3、第一个字符必须伟字母或下划线

    4、标识符中字母区分大小写

    #数据类型

    存在的意义:给变量分配一个合适的内存空间。

    sizeof关键字

    //可以利用sizeof求出数据类型占用的内存大小
    //语法:sizeof(数据类型、变量)

    实型

     默认输出一个小数,只会输出六个有效数字

    float f2 = 3e2;
    cout << f2<<endl; 300
    float f3 = 3e-2;
    cout << f3; 0.3

    1、单精度

    2、双精度

     

    字符型

    C/C++字符变量只存一个字节。

    字符型变量并不是把字符本身存到内存中,而是存的ascii值。

    转义字符

    加参数

    字符串型

    C语言风格字符串

    char 变量名[] = “字符串的值";

    C++语言风格字符串

    String 变量名=”字符串值“;

    布尔类型

    true=1;

    false=0;

    只占用1个字节。

    bool只要是非0都为真。

    三目运算符。

    创建三个变量abc

    将a和b做比较,将变量打的值赋值给C。

    int a = 10;
    int b = 20;
    int c = 30;
    c=(a > b ? a : b);

    switch语句

    缺点:判断时候只能是整形或字符型,不可以是一个区间。

    优点:结构清晰,执行效率高。

    一维数组创建

    //数组类型 数组名[数组长度];
    //数组类型 数组名[数组长度] = { 值1,值2,值3 };
    //数组类型 数组名[] = { 值1,值2,值3 };

    一维数组名称的用途:
    1、可以统计整个数组在内存中的长度
    2、可以获取数组中内存的首地址

    cout << (int)&arr[0] << endl;

    //将地址转为10进制

    二维数组定义方式:

    数据类型 数组名[行数][列数];

    数据类型 数组名[行数][列数]={{数据1,数据2},{数据3,数据4}}

    数据类型 数组名[行数][列数]={数据1,数据2,数据3,数据4};

    数据类型 数组名[][列数]={数据1,数据2,数据3,数据4}

    二维数组数组名

    1、查看二维数组所占内存空间

    2、获取二维数组的首地址

    int arr[2][3] = { 1,2,3,4,5,6 };
    cout << sizeof(arr)<<endl;
    cout << arr << endl;
    cout << sizeof(arr[0]) << endl;//一行所占的内存大小。
    cout << arr[0][0] << endl;

    函数

     函数的声明

    提前告诉编译器函数的存在,可以利用函数的声明。

    函数的分文件编写

    作用:让代码结构更加清晰

    1、创建后缀名为.h的头文件

    2、创建后缀名为.cpp的源文件

    3、在头文件写函数的声明

    4、在源文件中写函数的定义

    指针

    空指针和野指针

    空指针:指针变量只想内存中编号为0的空间

    用途:初始化指针变量

    注意:空指针只想的内存是不可访问的。

    C++核心编程部分

    1、内存分区模型

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

    运行的时候。

    1、代码区:存放函数体的二进制代码,由操作系统管理的

    2、全局区:存放静态变量和全局变量

    3、栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。(只要是局部都是栈区)

    4、堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

    意义:不同的区域存放的数据,赋予不同的生命周期,更灵活的编程。

    在程序编译后,未执行以前分成两个区域

    代码区:

      存放CPU执行的机器指令

      代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。

      代码区是只读的,使其只读的原因是防止程序意外地修改它的指令。

    全局区:

      全局变量和静态变量存放的区域

      全局区还包含了常量区,字符串常量和其他常量存放在此

      该区域的数据在程序结束后操作系统释放。

    总结:

    C++在运行之前,编译后,分为全局区和代码区

    代码区的特点是共享和只读

    全局区中存放全局变量、静态变量、常量

    常量区存放const修饰的全局变量 和字符串常量。

    不要返回局部变量的地址

    int* func() {//形参数据也放在栈区
    int a = 10;
    return &a;
    }

    int main() {
    int* p = func();
    cout << *p << endl;//第一次可以打印是因为,编译器做了保留
    cout << *p << endl;//第二次数据不再保留了。
    system("pause");
    return 0;
    }

    堆区:

    由程序员分配释放,若程序员不是放,程序结束的时候由操作系统回收

    在C++中主要利用new在堆区开辟内存

     指针本质也是局部变量,放在栈上,但是指针保存的数据是存放在堆区。

    #include <iostream>
    using namespace std;

    int* func() {
    int* a = new int(10);
    return a;
    }

    int main() {
    int* p = func();
    cout << *p << endl;// 在堆区开辟的数据
    cout << *p << endl;//
    system("pause");
    return 0;
    }

    new操作符

    C++利用new操作符在堆区开辟数据

    堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete

    语法:new  数据类型

    利用new创建的数据,会返回该数据对应的类型的指针。

    delete操作符

    堆区的数据 由程序员管理开辟 程序员管理释放

    如果想释放堆区的数据,利用关键字 delete释放

    int *p=new int(10);

    delete p;

    在堆区利用new开辟数组

    创建10整形数据的数组,在堆区

     int *arr=new int[10];//10代表数组有10个元素。

    释放数组的时候

    delete[] arr;

    引用

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

    int &b=a;

    b=20;

    cout<<a<<endl;// a为20

    int main() {
    int a = 10;
    int b = 20;
    //int &c;//错误,引用必须初始化
    int &c = a;//一旦初始化以后,就不可以更改
    c = b;//这是赋值操作,不是更改引用
    cout << " a = " << a << endl;
    cout << " b = " << b << endl;
    cout << " c = " << c << endl;
    system("pause");
    return 0;

    1、引用必须要初始化

    2、引用一旦初始化后,就不可以更改。

    对象初始化和清理

    如果不提供构造和析构,编译器会自动提供构造函数和析构函数,但是都是空实现。

    构造函数语法:类名(){}

    1、构造函数,没有返回值也不写void

    2、函数名称与类名相同

    3、构造函数可以有参数,因此可以发生重载

    4、程序在调用对象的时候回自动调用,无须手动调用,而且只会调用一次

    析构函数语法:~类名(){}

    1、析构函数,没有返回值,也不写void

    2、函数名称与类名相同,在名称前加符号~

    3、析构函数不可以有参数,因此不可以发生重载

    4、程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次。

    构造函数的分类:

    按参数分为:有参构造和无参构造

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

    三种调用方式:

    括号法

    显示法

    隐式转换

    //1、括号法
    Person person;
    Person person1(10);
    Person person2(person1);
    //2、显示法

    Person person1;
    Person person2 = Person(30);
    Person person3 = Person(person2);

    //3、隐式转换法
    Person person4 = 10;//相当于Person p4=person(10);

    4、拷贝构造函数调用时机

    C++中拷贝构造函数调用时机通常有三种情况

    1、使用一个已创建完毕的对象来初始化一个新的对象

    2、值传递的方式给函数参数传值

    3、以值方式返回局部对象

    构造函数调用规则

    C++编译器至少给一个类添加三个函数

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

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

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

    构造函数调用规则如下:

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

    如果用户定义拷贝构造函数(浅拷贝),C++不会在提供其他构造函数 。

    深拷贝&&浅拷贝

    Person p2(p1)

    如果利用编译器提供的拷贝构造函数,会做浅拷贝。

    //自己实现拷贝构造函数 解决浅拷贝问题
    Person(const Person &p) {
    cout << "自己写的拷贝构造函数" << endl;
    this->age = p.age;
    //this->m_Height = p.m_Height; 编译器默认实现是这一行代码 浅拷贝!!!
    //深拷贝
    this->m_Height = new int(*p.m_Height);
    }

    编译器创建的浅拷贝,会把上一个对象的属性地址给当前对象,如果当前对象析构释放该属性内存,前一个对象再析构,就会重复释放。造成浅拷贝问题!

    如果属性有在堆区开辟的,一定要在自己提供的拷贝构造函数,防止浅拷贝问题。

    初始化列表

    语法:构造函数():属性(值1),属性(值2),属性(值3)

    Person(int a,int b,int c):m_A(a),m_B(b),m_C(c);

    类对象作为类成员


    class Phone {
    public:
    Phone(string m_Phone) {
    cout << "Phone构造函数" << endl;
    this->m_Phone = m_Phone;
    }
    string m_Phone;

    };

    class Person {
    public:
    string m_name;
    Phone m_phone;
    Person(string name, string phone) :m_name(name), m_phone(phone)
    {
    cout << "Person构造函数" << endl;
    }

    };

    void test() {
    Person p("张三", "iPhone 13");
    //向构造成员对象。析构顺序,为构造的入栈。
    }

    静态成员变量

    1、所有对象共享同一份数据

    2、在编译阶段分配内存

    3、类内声明,类外初始化

    静态成员函数

    1、所有对象共享同一个函数

    2、静态成员函数只能访问静态成员变量。

    class Person {
    public:
    static int m_A;

    };

    int Person::m_A = 10;//静态成员变量,在类外初始化,不能在函数内初始化,不属于任何一个对象,1、通过对象进行访问,2、通过类名进行访问。静态成员变量也有访问权限。

    静态成员函数

    所有的对象共享同一个函数

    静态成员函数只能访问静态成员变量。(如果调用成员变量,不知道是调用的那个对象的成员变量 )。类外访问不到私有的静态成员函数。

    1、通过对象访问。 

    2、通过类名访问。

    class Person {
    public:
    int m_A;//非静态成员变量
    static int m_B;//静态成员变量
    void func() {};//非静态成员函数
    static void func() {};
    };

    void test01() {
    Person p1;
    cout << sizeof(p1) << endl;
    }

    int main() {
    test01();//对象为空的时候,编译器会分配一个字节的空间,声明一个变量就已经分配了空间所以为4. 静态变量,不属于类的对象,不在对象的空间。 非静态成员函数,不属于类对象上、静态成员函数也不属于类对象
    //调用
    system("pause");
    return 0;
    }

    C++对象模型和this指针。

    在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量属于类的对象上。

    public:
    int m_A;//非静态成员变量
    };

    void test01() {
    Person p1;
    cout << sizeof(p1) << endl;
    }

    int main() {
    test01();//对象为空的时候,编译器会分配一个字节的空间,声明一个变量就已经分配了空间所以为4.
    //调用
    system("pause");
    return 0;
    }

    //解决名称冲突

    //返回对象本身用*this

    class Person {
    public:
    int m_A;//非静态成员变量.成员变量前面加个m_ 代表是成员变量
    static int m_B;//静态成员变量
    void func() {};//非静态成员函数
    static void func() {};
    };

    this指向当前对象的指针,而*this指向的就是当前对象的本体。

    class Person {
    public:
    int m_age;
    Person(int age) {
    this->m_age = age;
    }
    Person PersonAddAge(Person &p) {//返回构造函数如果没有加引用,就是值的复制。
    this->m_age += p.m_age;

    return *this;
    }
    };

    void test01() {
    Person p1(10);
    Person p2(10);
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);

    cout << p2.m_age << endl;
    }

    #空指针访问成员函数

    不访问成员变量可以正确执行

    const修饰成员函数
    常函数:

    1、成员函数后加const后我们称为常函数

    2、常函数不可以修改成员属性

    3、成员属性声明加mutable后,在常函数中依然可以修改

    常对象:

    声明对象前加const称为常对象

    常对象不能修改成员属性。 

    常对象只能调用常函数(常对象访问非常函数,可能修改成员属性,所以不允许修改成员属性)

    class Person {
    public:
    mutable int m_age;
    Person(int age) {
    this->m_age = age;
    }

    /*this指针的本质,是指针常量, 指针的指向不可以修改的。 =person *const this 指向不可以更改 */
    void showPerson() const{//这个地方的const 加在相当于加在了this指针的前面=const person* const this
    m_age=100;
    }

    void showPersonAge() {
    cout << "age=" << this->m_age << endl;
    }

    };


    void test01() {
    const Person p;//常对象
    p.m_age = 100;//能改 特殊mutable
    p.m_ageb = 100;//不能更改。
    //p1->showPersonAge();
    }

    常对象只能调用常函数。

    友元

    三种实现:

    全局函数做友元

    类做友元

    成员函数做友元

    1、全局函数做友元

    //建筑物
    //全局函数做友元
    class Building {
    friend void goodGay(Building *build);//只有写在类里边。不用写public或者private
    public:
    Building() {
    m_SittingRoom = "客厅";
    m_BedRoom = "卧室";
    }
    string m_SittingRoom;//客厅。
    private:
    string m_BedRoom;//卧室
    };

    //全局函数
    void goodGay(Building *build) {
    cout << "好基友的全局函数,正在访问:" << build->m_SittingRoom << endl;
    cout << "好基友的全局函数,正在访问:" << build->m_BedRoom << endl;
    }


    void test01() {
    Building building;
    goodGay(&building);
    }

    2、类做友元

    public:
    void visit();//参观函数访问building中的私有属性
    Building *building;
    GoodGay();
    };

    class Building {
    friend class GoodGay;//在需要访问的类中,声明是友元类。
    public:
    Building();//在类外定义,需要在类中显示声明。
    string m_SittingRoom;//客厅
    private:
    string m_BedRooml;//卧室
    };

    //类外写成员函数
    Building::Building() {
    m_SittingRoom = "客厅";
    m_BedRooml = "卧室";
    }

    GoodGay::GoodGay(){
    //创建建筑物对象
    building = new Building();
    }

    void GoodGay::visit() {
    cout << "好基友正在访问:" << building->m_SittingRoom << endl;
    cout << "好基友正在访问:" << building->m_BedRooml << endl;
    }


    void test01() {
    GoodGay goodgay;
    goodgay.visit();

    }

    3、成员函数做友元

    class Building;
    class GoodGay {
    public:
    GoodGay();
    Building *building;

    void visit();//不可以访问Building中的私有成员
    void visit2();//不可以访问Building中的私有成员
    };

    class Building {
    public:
    friend void GoodGay::visit();//不可以访问Building中的私有成员
    friend void GoodGay::visit2();//不可以访问Building中的私有成员

    Building();
    string m_SittingRoom;//客厅
    private:
    string m_BedRoom;//卧室

    };

    GoodGay::GoodGay() {
    building = new Building();
    }

    void GoodGay::visit() {
    cout << "vist函数:" << building->m_SittingRoom << endl;
    cout << "vist函数:" << building->m_BedRoom << endl;
    }

    void GoodGay::visit2() {
    cout << "vist函数:" << building->m_SittingRoom << endl;
    cout << "vist函数:" << building->m_BedRoom << endl;
    }


    Building::Building() {
    m_SittingRoom = "客厅";
    m_BedRoom = "卧室";
    }

    void test() {
    GoodGay gg;
    gg.visit();
    }  

    友元定义的顺序问题:


    class GoodGay {
    public:
    GoodGay();
    Building *building;
    void visit();//不可以访问Building中的私有成员
    void visit2();//不可以访问Building中的私有成员
    };


    class Building {
    public:
    string m_SittingRoom;//客厅
    friend void GoodGay::visit();//不可以访问Building中的私有成员
    friend void GoodGay::visit2();//不可以访问Building中的私有成员
    Building();

    private:
    string m_BedRoom;//卧室
    };

    friend 出现的函数应该在前面出现过。

    不要滥用重载

    对于内置的数据类型的表达式的运算符是不可能改变的。1+1=2 不能1+1=0;

    //只能利用全局函数重载左移运算符 

    不然不能达到cout在左侧。

    ostream & operator<<(ostream &cout, Person &p) {
    cout << "ma=<<" << p.m_A;
    return cout; //链式编程
    }

    cout<<"1"<<“2”;

    在类中写

    friend ostream & operator<<(ostream &cout, Person &p);

    可以访问私有变量。

      

    赋值运算符重载

    C++编译器至少给一个类添加四个函数:

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

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

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

    4、赋值运算符operator=,对属性值进行拷贝

    如果类中又属性指向堆区,做赋值操作也会出现深浅拷贝的问题。


    class Person
    {
    public:
    int *m_Age;
    Person(int age) {
    this->m_Age = new int(age);
    }
    ~Person() {
    if (m_Age != NULL) {
    delete m_Age;
    m_Age = NULL;
    }
    }
    void operator=(Person &p) {
    //编译器提供的浅拷贝
    if (m_Age != NULL) {
    delete m_Age;
    m_Age = NULL;
    }
    this->m_Age = new int(*p.m_Age); //深拷贝
    }
    };

    函数调用运算符重载

    //打印输出类

    仿函数 重载了(),像调用了一个函数。

    继承

    继承的好处:减少 重复代码

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

    子类:派生类

    父类:基类

    继承方式:

    公共继承

    保护继承

    私有继承

    public: 能被类成员函数、子类函数、友元访问,也能被类的对象访问。
    private: 只能被类成员函数及友元访问,不能被其他任何访问,本身的类对象也不行。
    protected: 只能被类成员函数、子类函数及友元访问,不能被其他任何访问,本身的类对象也不行(类外)。

    //继承方式
    class base1 {
    public:
    int m_A;
    protected:
    int m_B;
    private:
    int m_C;
    };

    class Son :public base1 {
    public:
    void func() {
    m_A = 10;//父类中的公共权限能够访问
    m_B = 10;//父类中的保护权限能够访问,父类中的保护,到子类依然是保护权限
    //m_C = 20;//不可访问。父类中的私有权限成员子类访问不到

    }
    };

    class Son2:protected base1
    {
    public:
    void func() {
    m_A = 100;
    m_B = 100;
    //m_C = 100;不可访问私有
    }
    private:

    };

    class Son3 :private base1 {
    public:
    void func() {
    m_A = 100;
    m_B = 100;
    m_C = 100;//不可访问私有
    }
    };


    void test01() {
    Son son1;
    son1.m_A = 100;
    son1.m_B = 20;//类内能访问,类外也不能访问。
    Son2 son2;
    son2.m_A;//类内能访问,类外不能访问
    Son3 son3;
    son3.m_A//类内能访问,类外不能访问。
    }

    继承中的对象模型

    //继承方式
    class base1 {
    public:
    int m_A;
    protected:
    int m_B;
    private:
    int m_C;
    };

    class Son :public base1{
    public:
    int m_D;
    };

    //利用开发人员命令提示工具查看对象模型
    //跳转盘符F:
    //跳转文件路径 cd 具体路径下
    //cl /d1 reportSingleClassLayout类名 文件名

    void test01() {
    cout << "size of son" << sizeof(Son) << endl; //16 父类中所有非静态的成员,父类私有变量被编译器隐藏了 。继承的变量,和自身新增的变量。
    }

    继承中构造和析构顺序

    子类继承父类后,当创建子类对象,也会调用父类的构造函数

    //继承方式
    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 test() {
    //Base b;
    Son son;
    }//构造函数顺序 base构造 son构造 son析构 base析构

    继承同名成员处理方式

    问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类的同名数据?

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

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

    class Son :public Base {
    public:
    int m_A=20;
    Son() {
    cout << "Son构造函数" << endl;
    }
    ~Son() {
    cout << "Son析构函数" << endl;
    }
    };

    void test() {
    //Base b;
    Son son;
    son.m_A;
    cout << son.m_A << endl;//访问子类
    cout << son.Base::m_A << endl;//访问父类 同名变量。 同名函数同理
    }

    如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数,包括有参。

    如果要访问,需要加作用域

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

    继承中同名的静态成员子类对象上如何进行访问

    静态成员和非静态成员出现同名,处理方式一致

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

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

    1、通过对象访问

    2、通过类名访问

    SON::Base::m_A 第一个代表通过类名方式访问 第二个::代表父类作用域下

    多继承语法

    C++允许一个类继承多个类

    语法:class 子类:继承方式 父类1,继承方式 父类2

    多继承可能回引发父类中有同名成员出现,需要加作用域区分

    C++实际开发不建议用多继承

     

     菱形继承导致资源浪费的解决方案:

    利用虚继承 解决菱形继承的问题

    继承之前 加上关键字virtual变为虚继承

    animal 类称为虚基类

    //利用多态实现计算器
    class AbstractCalculator {
    public:
    virtual int getResult() {
    return 0;
    }
    int m_Num1;
    int m_Num2;
    };


    class AddCalculator :public AbstractCalculator {
    public:
    virtual int getResult(){
    return m_Num1 + m_Num2;
    }
    };

    class SubCalculator :public AbstractCalculator {
    public:
    virtual int getResult() {
    return m_Num1 - m_Num2;
    }
    };

    class MulCalculator :public AbstractCalculator {
    public:
    virtual int getResult() {
    return m_Num1 * m_Num2;
    }
    };

    void test02() {
    //多态使用条件
    //父类指针或者引用指向子类对象
    AbstractCalculator *abc = new AddCalculator();
    abc->m_Num1 = 10;
    abc->m_Num2 = 20;
    cout << abc->getResult() << endl;;
    abc = new SubCalculator();
    abc->m_Num1 = 10;
    abc->m_Num2 = 20;
    cout << abc->getResult();


    delete abc;
    }

    int main() {
    test02();
    }

  • 相关阅读:
    类的继承,抽象类和接口
    什么是CGI、FastCGI、PHP-CGI、PHP-FPM、Spawn-FCGI?
    php 中 SERVER 服务器参数
    数组与对象互换方法
    php实现二维数组查找功能【array_search 和 array_column】
    php基础知识点列表【2020年10月7日】
    json_encode 中文及特殊斜杆的编码
    吴裕雄--天生自然ANDROID开发学习:2.5.8 Notification(状态栏通知)详解
    吴裕雄--天生自然ANDROID开发学习:2.5.7 Toast(吐司)的基本使用
    吴裕雄--天生自然ANDROID开发学习:2.5.6 ViewFlipper(翻转视图)的基本使用
  • 原文地址:https://www.cnblogs.com/Alei777/p/15293854.html
Copyright © 2020-2023  润新知