C++学习记录

new

        //为a赋值为10

        int a = new int(10) 

        int * arr =new int [10]

delete

        delete a

        delete[ ]  arr

当你使用 delete 关键字删除一个动态分配的对象时,会调用该对象的析构函数。

引用

        本质——本质是个指针常量,int  * const ref = &a;

        基本语法

                int a=10;

                int & b =a

        注意事项

·                必须初始化

                初始化后不能修改

        常量引用

                避免在使用的过程中被修改

                可以使类似10的常量被引用;int & ref = 10;

        做函数参数

                可以像地址传递一样交换值大小

        做返回值

                不要返回局部变量引用。函数结束后,栈区内存释放,局部变量消失,引用将成为类似野指针的存在

函数

        默认参数    func(int a,int b = 10)

        占位符        func(int a , int )

函数重载

        取决于参数列表中的:参数类型,参数个数,参数顺序

访问权限

        public:类内外都可以访问,可以通过对象和成员方法访问

        protected:类内可以访问,可以被子类继承。可以通过成员方法访问

        private:类内可以访问,父类单独享有。只能通过成员方法访问

        注意: 在C++中,术语“内部”和“外部”通常用于描述类的成员函数对类成员的访问权限。

  • 内部成员函数:内部成员函数是指属于同一个类的成员函数,在这些成员函数中,可以直接访问该类的所有成员,包括私有成员。因此,内部成员函数对类的所有成员都有访问权限。

  • 外部成员函数:外部成员函数是指不属于该类的成员函数,在这些函数中,只能通过类的公有接口来访问类的成员,不能直接访问类的私有成员。外部成员函数只能访问公有成员和保护成员,不能访问私有成员。

        这里的“内部”和“外部”主要指的是函数在类的内部定义(成员函数)还是在类的外部定义(非成员函数)。内部成员函数可以直接访问私有成员,而外部成员函数不能直接访问私有成员。

struct和class在c++的唯一区别:访问权限的不同。class默认私有,struct默认公开

构造函数         类名(){}

        调用规则

                一开始提供午餐构造,有参构造,拷贝构造

                手写有参构造后,不在提供无参构造

                手写拷贝构造后,不再提供其它构造

析构函数         ~类名(){}

拷贝构造函数的调用时机 类名(const 类名 &p)

        注意:使用引用是为了避免无限循环。例如在以下第一种情境中,调用拷贝构造函数时,如何还是值传递,就会再一次调用拷贝构造函数,造成无限循环

        1.用已经创建的对象 对对象 初始化

        2.值传递为函数参数的时候

        3.值传递返回局部变量的时候

        本质上都是创建副本所需。

深拷贝,浅拷贝

        主要针对的是堆区储存的内容

        对于浅拷贝,如果已经释放一次的内容再次被释放,就会出错

        深拷贝通过重写拷贝构造函数,解决了这一问题。

                eg.       height = new int(*p.height) 在堆区重新申请一篇内存储存数据,即使原本的数据被释放,深拷贝的数据仍在堆区存在。

初始化列表        构造函数():A(),B(),C(){}

        如果一个类的普通数据(类似ABCD)有很多,一个一个赋值会很麻烦,初始化列表可以简化这个过程;

类对象作为类成员时,构造和析构的运行顺序

        class A{

         B b;

}

        构造函数,先调用B的构造函数,在调用A的构造函数

                为了保证构造A的时候,B已经完成初始化。

        析构函数,先调用A的析构函数,再调用B的构造函数

                为了保证析构A的时候,B还在,还可以对其进行操作

静态成员(变量/函数)

        可以通过类名/对象进行数据的访问

        静态变量:类内定义,类外初始化 int person::A =100

        静态函数:只能访问静态成员变量。因为静态成员函数不属于任何特定的对象实例,而是属于类本身。因此,静态成员函数在执行时没有隐式的 this 指针指向对象实例,无法直接访问非静态成员变量,因为非静态成员变量是属于对象实例的。

        只有非静态成员变量,才属于类的对象

this指针的用途

        1.避免成员变量和形参名字冲突

        2.链式编程,返回用return *this(用引用的方式返回)。

                此处涉及的问题是,如果不用引用的方式返回,将会返回一个P的副本(此处称为P'),那么链式编程中,下一次调用函数的就不再是P,而是P'。这将会造成P的预期结果由执行三次的40——变为只执行一次的结果20.

const修饰的 常函数,常对象

        mutable修饰的变量仍然可以修改

        成员函数本身隐含有this指针(指针常量,方向已经不可改变),再加上const修饰即不可改变值和方向

        常对象只能调用常函数。因为调用其它成员函数的时候,可以改变成员变量的值,和定义不符。

友元(关于你的基友想要进入你房间的这件事)

        1.全局函数作友元

        2.友元类——类作友元

        3.成员函数作友元

        刚开始我纠结的是:课程中用的是指针指向对象,书本用的是引用。但是实际上两者下过是一样的,代码大致上相同

运算符重载

一般有成员函数重载/全局函数两种方式

        加号—— 返回值类型 operator+(),两种方式都可以

        左移运算符——cout是ostream的一个对象,只能用引用传递

      

        不能用成员函数的方法重载。因为这么做的话,p<<cout与预计的cout<<p顺序相反

        只能用全局函数重载 返回值类型 operator<<(ostream cout,person p)。又因为<<相当于链式编程,前面说到过,(P,P',P"导致结果为20,而不是40那里)所以返回值必须为cout 即返回值类型为ostream

        递增运算符

        在左移运算符重载的基础上进行

        前置递增

基本逻辑
void operator++(){
    A++;
}

对于基本类型变量,可以连续自加
int a =0;
cout<<++(++a)<<endl;
//a=2

为了实现这样的效果,需要将自加后的对象返回
MYINT& operator++(){
    A++;
    return *this;
}
这里用引用的方式返回有两个理由:第一,为了防止值传递返回副本。虽然这次结果输出正确,但之后再调用A的值会显示1(只有一次前置自加是对A,第二次操作相当于A');第二,这次返回的是对象本身(*this),本身就需要自身的值

        后置自增

首先,如何与前置自增区分?——占位符

基本逻辑
void operator++(){
    MYINT temp = *this;//用临时变量保存原始值
    A++;
    return temp;
}

与连续前置自增不同,(A++)++是非法的。
这样的话,就不必纠结返回值类型(毕竟不连续用,这一次没问题就行)

完善逻辑
MYINT operator++(){    此处用值的方式返回temp,因为temp是局部变量,完成后置自增后,temp就要从内存中去除的
    MYINT temp = *this;
    A++;
    return temp;
}

        赋值运算符重写

目的
1.解决浅拷贝的问题
2.实现赋值运算符的连等操作

基本逻辑
person& operator=(person &p){这里必须使用引用的方式
如果用副本做参数,那么副本的指针部分将进行浅拷贝
这样一来,跳出此函数之后,原本有值的堆区就被释放了

    if(m_age!=null){
    delete m_age;
    m_age = null;
    }
    提供深拷贝
    m_age=new int(*p.m_age)
    实现连等
    return *this;
    与实现连续自加一样,以引用返回对象本身
}

 什么时候用&,什么时候值传递

——我知道两种

——1.不想被修改的用值传递,允许被修改的用&

——2.局部变量,重载符号连用等特殊情况

         关系运算符重载

        这里举例子的是==和!=,简单的很,就不做赘述了

        仿函数——重载()的成员函数

在学习STL之前,可能会认为仿函数和普通函数之间的区别不太明显,因为仿函数并不是初学者通常接触到的概念。在初学者阶段,可能更多地关注函数的基本使用和语法。

但是,一旦开始学习STL(标准模板库),特别是学习算法部分时,仿函数的重要性就会显现出来。STL的许多算法都可以通过传递仿函数来实现定制化的操作,比如排序算法中的比较、查找算法中的判断等。在这些场景下,仿函数的灵活性和可定制性就会得到充分的展现。

因此,尽管初学者可能觉得仿函数和普通函数之间的区别不大,但在学习STL并深入了解算法和数据结构时,了解仿函数的概念和用法会变得至关重要。

        继承

        基本语法——class student :继承方式 父类类名{    }

        父类中的私有,子类都访问不到

        ——虽然访问不到,但是这部分仍然被子类继承了(只是被隐藏了)

        继承方式有 public,protected,private三种。

每种方式都是继承过来的成员变量的最低权限;只有public可以在全局函数中(类外)访问,private和protected只能在类内访问;

        构造和析构的顺序

与类对象做类成员不同:“构造——先内后外;析构——先外后内”。此处的顺序为:“构造——先父类后子类;析构——先子类后父类”

        同名成员

——作用域立大功

        同名静态成员

——作用域立大功。不同的是,有两种方式可以访问:“通过对象”,“通过类”

        菱形继承

——虚继承:继承方式前+virtual()

虚继承的实现涉及了一些底层机制,其中一个重要的概念是虚基表(vtable)和虚基指针(vptr)。

  1. 虚基表(vtable):每个包含虚函数或虚基类的类都会有一个虚函数表(vtable),虚基类的信息也存储在这个表中。虚基表中存储了虚基类的偏移量,用于在派生类中定位虚基类的成员。

  2.  虚基指针(vptr):对于包含虚函数或虚基类的每个类的对象,都会有一个指向虚表的指针,称为虚指针(vptr)。虚指针位于对象的内存布局的开头,它指向对象所属类的虚表。

在使用虚继承时,派生类中会包含一个指向虚基类子对象的偏移量,这个偏移量存储在派生类对象中的虚表中。这个偏移量用于定位虚基类子对象在派生类对象中的位置。

当通过派生类对象访问虚基类成员时,编译器会使用虚指针和虚基表来定位虚基类成员的实际地址。这样,即使派生类中包含多个虚基类子对象,也可以正确地访问每个虚基类的成员。

总的来说,通过虚指针和虚表的机制,编译器能够在派生类对象中正确地定位和访问虚基类的成员,从而实现了虚继承的功能。

多态

基本语法

原理

虚函数和纯虚函数

——已经知道虚函数在父类中通常是无意义的。

纯虚函数:俺来啦!!!!

——值得注意的是,没有具体的函数实现(要么怎么叫纯虚呢hhh)

——这里第一次出现抽象类的概念(虽然学java的时候也接触过。我的理解就是,不能实例化对象,天生就是被用的)

虚析构和纯虚析构

——继承中我们接触过构造函数和析构函数的顺序。但是在多态会遇到一个新的问题:“析构函数不调用子类析构,只调用父类析构”,这会导致子类在堆上的内容清理不干净。

——需要注意的是虚析构和纯虚析构需要有函数实现部分。

文件(带我回校翻翻教材,不记得要不要学了)

泛型编程——模板(明确目的:不是为了写模板,而是为了运用STL)

概念

——提高代码的复用性。模板只是框架,不能直接使用。

函数模板

基本语法

——template<typename T>

——使用方式

注意事项

1.自动类型推导,必须推导出一致的数据类型T

2.模板必须指定T的数据类型才可以使用(???)

(有时可以依靠自动类型转换而忽略“指定T”这一操作),尽管这样,这一步依然存疑,感觉是多余的操作。

普通函数与函数模板的区别

优先显示指定类型,因为我们明明知道他要用什么,省的编译器到时候又大惊小怪

——注意事项中提到过,自动类型推导必须推出与‘T’数据类型一致才行。

之前的理解:“int先有,后来进入的char不被识别为T的数据类型”

正确的理解:“既有int,又有char,T并不知道谁才是对的,所以报错”

普通函数与函数模板的调用规则

空模板参数:“<>”

模板的局限性

这种问题与“比较两个类对象的大小”是同样的问题。之前通过运算符重载解决,这次通过具体化模板实现。

类模板

模板必须指定T的数据类型才可以使用”在这里可以有新的理解:“之前显示不出必要性,这里则显示的淋漓尽致。实际上是为了符合规范“使用显示指定类型”。”

类模板与函数模板的区别

第一条,合理的解释了上一条“选择使用显式指定类型”的原因——类模板没有自动类型推导

第二条,类模板的模板参数列表中可以有默认参数。为什么模板函数中不可以呢?因为模板函数的T只有一个,只要有一个确定类型(比如int),那其他的也都定死了,相当于把一个模板函数写死成普通函数了。

类模板与函数模板成员函数的调用时机

截至到P177

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值