前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++核心编程学习

C++核心编程学习

原创
作者头像
买唯送忧
修改2021-09-13 11:04:56
3550
修改2021-09-13 11:04:56
举报
文章被收录于专栏:虚拟技术学习虚拟技术学习

一、内存四区

1、程序运行前(只存在两个区)

(1)代码区

主要是存放CPU执行的机器指令,特点是共享,只读

(2)全局区

包含常量区;主要是存放全局变量,静态变量,字符串常量,const修饰的全局变量, 不包括const修饰的局部变量区域的数据在程序结束后由操作系统释放

2、程序运行后(新加了两个区)

(1)栈区

栈区的数据由编译器负责管理开辟和释放;(不要返回局部变量的地址);;局部变量,函数形参,栈区的数据在函数执行完后自动释放;

(2)堆区

由程序员分配释放,若程序员不释放,操作系统会回收;在C++主要使用new申请空间;new ===>delete,,,,new[]====>delete[]

注意::对于char p[10] = "hello!!!\n"可以对p进行修改,因为p是字符数组,存放在栈区,所以可以进行修改,但是如果是这样的char *p = "hello!!!\n"那么就不能进行修改,因为p是一个指向常量区的指针,所以p指针指向的内容是不能变的;如果这样写:char *p = new char[10]; strcpy(p, "hello!!!\n");p[2] = d;这样也不会发生错误,因为在堆上申请了空间;

二、引用

1、引用必须要初始化

在定义引用要初始化,不然就是不合法,因为引用就是为一个变量或者常量定义别名;int a = 10; int &b = a;在这里b就是a的别名,操作b就是相当于操作a; const int &c = 10;

2、引用初始化之后不能更改

就是定义引用之后,不能在对引用变量进行取地址并且赋值操作:如以下就是不合法:int a = 10; int &b = a; int c = 233; &b = c

3、做函数参数,可以达到指针效果

4、引用做函数返回值,可以作为左值

5、引用的本质在C++内部实现是一个指针常量(指针不可变, 指针指向的内容可以变)int* const ref(指针常量)

6、常量引用:用来修饰形参,防止误操作

三、函数

1、函数默认参数

int func(int a, int b = 100, int c = 100)之后调用的时候可以使用以下调用方式:func(10), func(100, 100), func(100, 1000, 1000)

在使用函数参数的时候需要注意以下两个点:函数声明和定义不能同时用默认参数,最多只能有一个有;在使用默认参数时,第一个默认参数后面必须都是默认参数,才是合法如int func(int a = 10, int b, int c)是不合法的!!!

2、函数占位参数

有变量类型,但是无变量名:::int func(int a, int)

占位参数也可以是默认参数:::int func(int a, int = 10)

3、函数重载

同一个作用域下, 函数名称相同,函数参数,类型,个数,顺序不同;;;;函数返回值不能作为重载条件

int func(const int & a);与int func(int& a)也是重载当实参是变量,优先调用后者,反之调用前者

int func(int a, int b = 10); int func(int a)当实参是一个参数会产生二义性,导致错误,,如果两个实参就不会报错

四、类

1、struct与class的区别

class里默认数据权限是private, struct默认数据权限是public

2、构造函数和析构函数

析构函数无参数,不可发生重载, 构造函数可以有参数,,允许重载;

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

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

调用方式:括号法(Person p()), 显示法, 隐式转换法(可以看之前的一片文章关于转换函数的);

3、拷贝函数的调用时机

使用已经创建完毕的对象来初始化一个新对象:Person p1(10); Person p2(p1);

值传递的方式给函数参数传值:void doWork(Person p){} int main(){Person p1, doWork(p1); //会调用拷贝构造函数},为什么呢?因为值传递的话,,会复制一份p1到doWork的形参中,而不是原来的p1,所以相当于是用p1拷贝构造了一个变量

值传递返回局部对象: Person doWork(){Person p1; return p1 //这里也会调用拷贝构造},意思跟值传递一样的意思

4、构造函数的调用规则

默认情况下:默认构造函数, 默认析构函数, 拷贝构造函数都会默认提供

如果用户自己定义了有参构造函数, 那么编译器不会提供默认构造函数;但是会提供拷贝构造函数;

如果用户自己定义了拷贝构造函数, 那么编译器其他的构造函数都不会提供;

5、深拷贝和浅拷贝

在进行拷贝构造函数的时候,相当于是把一个对象的所有内容复制到了另一个对象之中,但是如果在原来的对象里存在堆区的数据,也就是程序员自己申请了堆区的空间,就会产生问题:因为在执行完之后,要手动释放堆区空间,,现在有两个对象指向同一片堆空间,如果其中一个释放了堆空间,另一个再来释放就会报错,这种错误就是浅拷贝导致的;为了解决这种问题,就要引入深拷贝,也就是在拷贝构造的时候,先把堆空间释放,,再自己申请一个新的堆区,这样就可以不用两个对象共用一个堆空间;

浅拷贝:简单的赋值操作;就会导致堆区的空间的重复释放;;

深拷贝:(如果有堆区空间)在拷贝赋值里面自己另外申请一个空间;;Person(const Person& p){m_height = new int(*p.m_height);}

6、初始化列表

构造函数():属性1(值1), 属性2(值2), 属性3(值3)...{};

7、类对象作为类成员

构造函数及其析构函数的顺序:构造函数::由里到外,,,, 析构函数::由外到里

8、静态成员

静态成员变量:所有对象共享同一个数据, 在编译阶段分配内存,类内声明static int num;,类外初始化:int Person::num = 0

静态成员函数:所有对象共享同一个函数, 静态成员函数只能访问静态变量;

通过对象访问:Person p; p.func();

通过类名: Person::func()

9、成员变量和成员函数分开存储(只有非静态成员才属于类对象上的)

Person p; 空对象占用的内存空间为1;C++编译器会给每个空对象也分配一个字节, 是为了区分空对象占内存的位置; 每个空对象都有个独一无二的内存地址;

10、this指针:this指针指向被调用的成员函数所属的对象;

解决名称冲突:Person(int age){this->age = age;}

返回对象本身用*this:

Person& addAge(Person p){

this->age += p.age;

return *this;

}

Person p1(10);

Person p2(0);

p2.addAge(p1).addAge(p1);===>p2.age = 30;

如果addAge函数的返回值把引用去掉,,那就是会调用一个新对象的拷贝构造,,,从而不再是p2了,最后结果就是20;

Person p = NULL; 如果没有涉及到访问非静态成员,那就是合法的;;;

11、const修饰成员函数

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

常函数内不可以修改成员属性;

成员属性声明前加关键字mutable后,在常函数中依然可以修改;

声明对象前加const称该对象为常对象,常对象只能调用常函数;

12、友元(目的就是让一个函数访问类内的私有成员)

(1)全局函数做友元:

在类内任意位置:friend void func(const Person& p);

在全局区域:void func(const Person& p){})

(2)其他类成员函数做友元:

在类内任意位置:friend void GoodGay::func(const Person& p);

在类外:void GoodGay::func(const Person& p){}

(3)其他类做友元

在类内的任意位置:friend class GoodGay;

在类外:class GoodGay{...}

13、运算符重载

代码语言:c++
复制
#ifndef __MYCLASS_H_
#define __MYCLASS_H_

#include<iostream>
#include<string>
using namespace std;

class Complex{
public:
    Complex(int _m_rel = 0, int _m_vir = 0) :m_rel(_m_rel), m_vir(_m_vir){}
    //成员函数重载
    Complex& operator +=(const Complex& c1);
    Complex& operator -=(const Complex& c1);
    Complex& operator++(); //前置++ ++Complex,返回引用
    Complex operator++(int); //后置++,返回值
    //赋值运算符的重载,如果涉及到堆区内存,,要先释放原来的堆区空间,,再来分配新赋值的空间,防止浅拷贝

    //函数调用运算符重载;调用的时候直接类名(字符串),或者类对象(字符串)
    void operator()(string test){
        cout << test << endl;
    }
    int getRel()const{return m_rel;}
    int getVir()const{return m_vir;}
    void test_print();
private:
    int m_rel, m_vir;
    friend Complex operator +(const Complex& c1, const Complex& c2);
    friend Complex operator -(const Complex& c1, const Complex& c2);
    friend Complex operator *(const Complex& c1, const Complex& c2);
    friend Complex operator /(const Complex& c1, const Complex& c2);
    friend bool operator == (const Complex& c1, const Complex& c2);
    friend bool operator != (const Complex& c1, const Complex& c2);
    friend ostream& operator <<(ostream& os, const Complex& c1);
    friend istream& operator >>(istream& is, const Complex& c1);

};

inline Complex operator +(const Complex& c1, const Complex& c2){
    return Complex(c1.m_rel + c2.m_rel, c1.m_vir + c2.m_vir);
}

inline Complex operator -(const Complex& c1, const Complex& c2){
    return Complex(c1.m_rel - c2.m_rel, c1.m_vir - c2.m_vir);
}

inline Complex operator *(const Complex& c1, const Complex& c2){
    return Complex(c1.m_rel * c2.m_rel, c1.m_vir * c2.m_vir);
}

inline Complex operator /(const Complex& c1, const Complex& c2){
    return Complex(c1.m_rel / c2.m_rel, c1.m_vir / c2.m_vir);
}

inline bool operator == (const Complex& c1, const Complex& c2){
    if (c1.m_rel == c2.m_rel && c1.m_vir == c2.m_vir){
        return true;
    }
    return false;
}

inline bool operator != (const Complex& c1, const Complex& c2){
    if (c1.m_rel != c2.m_rel || c1.m_vir != c2.m_vir){
        return true;
    }
    return false;
}

inline Complex& Complex::operator += (const Complex& c1){
    this->m_rel += c1.m_rel;
    this->m_vir += c1.m_vir;
    return *this;
}

inline Complex& Complex::operator -= (const Complex& c1){
    this->m_rel -= c1.m_rel;
    this->m_vir -= c1.m_vir;
    return *this;
}

inline ostream& operator <<(ostream& os, const Complex& c1){
    return os << "[" << c1.m_rel << "," << c1.m_vir << "]" << endl;
}

inline istream& operator >> (istream& is, const Complex& c1){
    return is >> c1.m_rel >> c1.m_vir;
}
inline Complex& Complex::operator ++(){
    ++this->m_rel;
    ++this->m_vir;
    return *this;
}

inline Complex Complex::operator ++(int){
    Complex old = *this;
    this->m_rel ++;
    this->m_vir ++;
    return old;
}



#endif

14、继承

(1)语法形式:class Son : public father,子类组成:一部分从基类继承而来,一部分是自己的东西

(2)继承方式:

公共继承(class B : public A):除了private不能子类不能访问, 其他类型子类都可以访问父类,外界只可以访问public类型;

保护继承(class B : protected A):除了private不能访问,其他都能, 但是外界一个数据都不能访问B;

私有继承(class B : private A):除了private不能访问,其他都能, 但是外界一个数据都不能访问B;

(3)继承中的对象模型

在父类中,所有的非静态成员变量(包括private)都要继承到子类(不论权限);只是在子类访问的时候,编译器会把private类型的数据隐藏起来

先构造父亲,再构造儿子;;先析构儿子,再构造父亲

(4)继承中同名成员的处理方式

同名成员变量:访问子类中的,自己点后面接变量名就行Son s; s.m_A, 访问父类中的需要加作用域; Son s; s.Base::m_A,;

同名成员函数:(在没有重载的时候),跟同名成员变量一样;如果子类中出现了父类的成员函数同名的成员函数,子类会自动隐藏父类所有的同名函数,要想访问就要加作用域;;;;

同名静态成员:处理方式与同名非静态成员一致;通过类名:子类:Son::m_A,, 父类:Son::Base::m_A;

多继承可能会引发多个同名成员,需要加作用域才能访问;class A: public B1, public B2;

(5)菱形继承

问题:(1)羊继承了动物数据, 驼也继承了动物数据,当羊驼使用动物数据,会产生二义性;(加作用域)

(2)羊驼继承了两份动物数据,造成重复情况:造成了资源浪费(利用虚继承,在继承之前加入virtual)

class Sheep : virtual public animal{}., class Tou : virtual public animal{};vbptr(虚基类指针):指向虚基类表

15、多态

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

(2)动态多态的条件:(1)有继承关系, (2)子类重写父类的虚函数,(3)一般在父类的指针或者引用,执行子类对象使用

(3)静态多态地址早绑定:在编译阶段;;;;动态多态地址晚绑定:在运行阶段

代码语言:javascript
复制
#include<iostream>
using namespace std;

class Animal{
public:
    void doSpeak(){
        cout << "动物在说话" << endl;
    }
};

class Cat : public Animal{
public:
    void doSpeak(){
        cout << "小猫在说话" << endl;
    }
};

class Dog : public Animal{
public:
    void doSpeak(){
        cout << "小狗在说话" << endl;
    }
};

void doSpeak(Animal & animal){
    animal.doSpeak();
}

void test1(){
    cout << "sizeof(Animal) = " << sizeof(Animal) << endl;  

int main(){
    Cat cat;
    Dog dog;
    doSpeak(cat);
    doSpeak(dog);
    test1();
    return 0;
}

//以上代码的输出程序很明显:因为这是静态动态,地址在编译阶段就已经确定,所以使用animal的类成员函数,
//调用的肯定都是animal类里的doSpeak函数,所以输出两个动物在说话;;而且此时animal类的大小为1;

//如果在animal类里面的doSpeak的返回值前加入一个virtual,那就成了动态多态:
//虽然使用的还是animal.doSpeak(),但是地址是在运行时绑定的,所以这个绑定的地址其实就是实参这里new的空间地址,
//所以就是new什么输出什么,所以输出一个小猫在说话,一个小狗在说话
//此时animal类的大小是4;虽然Animal类里无非静态成员变量,但是加了virtual后,,就会多一个指针
                               // (vfptr:虚函数指针)
                        //指向vftable(记录虚函数地址)

(4)纯虚函数与抽象类

纯虚函数:virtual 返回值 函数名(参数列表) = 0;

只要有一个纯虚函数的类,被称为抽象类;

抽象类特点:无法实例化对象, 抽象类的子类必须要重写父类中的纯虚函数,否则子类也为抽象类;

(5)虚析构和纯虚析构

多态使用时, 如果子类中有属性开辟到堆区, 那么父类指针在释放时无法调用到子类的析构代码;(即:子类的析构函数不会被调用)

上面问题解决:将父类中的析构函数改为虚析构或者纯虚析构即可;(纯虚析构函数也要实现,在外界实现)

(6)以下为一个多态小例子

代码语言:javascript
复制
#include<iostream>
using namespace std;

class AbstaractDrink{
public:
    virtual void Boil() = 0; //煮水
    virtual void Brew() = 0; //冲泡
    virtual void PourInCup() = 0; //倒入杯中
    virtual void PutSomeThing() = 0;//加入辅料

    void makeDrink(){
        Boil();
        Brew();
        PourInCup();
        PutSomeThing();
    }
};

class Coffee : public AbstaractDrink{
public:
    void Boil(){
        cout << "煮农夫山泉" << endl;
    }

    void Brew(){
        cout << "冲泡咖啡" << endl;
    }

    void PourInCup(){
        cout << "倒入咖啡杯中" << endl;
    }

    void PutSomeThing(){
        cout << "加入牛奶 " << endl;
    }
};

class Tea : public AbstaractDrink{
public:
    void Boil(){
        cout << "煮百岁山" << endl;
    }

    void Brew(){
        cout << "冲泡茶叶" << endl;
    }

    void PourInCup(){
        cout << "倒入茶杯中" << endl;
    }

    void PutSomeThing(){
        cout << "加入枸杞 " << endl;
    }
};

void doWork(AbstaractDrink *abs){
    abs->makeDrink();
    delete abs;
}

int main(){
       cout << "-----制作咖啡------" << endl;
       doWork(new Coffee);
       cout << "-----制作茶叶------" << endl;
       doWork(new Tea);
       return 0;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、内存四区
    • 1、程序运行前(只存在两个区)
      • (1)代码区
      • (2)全局区
    • 2、程序运行后(新加了两个区)
      • (1)栈区
      • (2)堆区
  • 二、引用
    • 1、引用必须要初始化
      • 2、引用初始化之后不能更改
        • 4、引用做函数返回值,可以作为左值
          • 5、引用的本质在C++内部实现是一个指针常量(指针不可变, 指针指向的内容可以变)int* const ref(指针常量)
            • 6、常量引用:用来修饰形参,防止误操作
            • 三、函数
              • 1、函数默认参数
                • 2、函数占位参数
                  • 3、函数重载
                  • 四、类
                    • 1、struct与class的区别
                      • 2、构造函数和析构函数
                        • 3、拷贝函数的调用时机
                          • 4、构造函数的调用规则
                            • 5、深拷贝和浅拷贝
                              • 6、初始化列表
                                • 7、类对象作为类成员
                                  • 8、静态成员
                                    • 9、成员变量和成员函数分开存储(只有非静态成员才属于类对象上的)
                                      • 10、this指针:this指针指向被调用的成员函数所属的对象;
                                        • 11、const修饰成员函数
                                          • 12、友元(目的就是让一个函数访问类内的私有成员)
                                            • (1)全局函数做友元:
                                            • (2)其他类成员函数做友元:
                                            • (3)其他类做友元
                                          • 13、运算符重载
                                            • 14、继承
                                              • (1)语法形式:class Son : public father,子类组成:一部分从基类继承而来,一部分是自己的东西
                                              • (2)继承方式:
                                              • (3)继承中的对象模型
                                              • (4)继承中同名成员的处理方式
                                              • (5)菱形继承
                                            • 15、多态
                                              • (1)静态动态:函数重载和运算符重载属于静态多态,复用函数名;
                                              • (2)动态多态的条件:(1)有继承关系, (2)子类重写父类的虚函数,(3)一般在父类的指针或者引用,执行子类对象使用
                                              • (3)静态多态地址早绑定:在编译阶段;;;;动态多态地址晚绑定:在运行阶段
                                              • (4)纯虚函数与抽象类
                                              • (5)虚析构和纯虚析构
                                              • (6)以下为一个多态小例子
                                          领券
                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档