c++基础

发布于:2025-02-14 ⋅ 阅读:(29) ⋅ 点赞:(0)


=========================================================================

1.什么是虚函数

虚函数只能借助于指针或者引用来达到多态的效果。

定义一个函数为虚函数,不代表函数为不被实现的函数。

定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。

定义一个函数为纯虚函数,才代表函数没有被实现。

定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。


当基类中的虚函数的返回值类型是基类类型的指针时,允许在派生类中重定义该虚函数时将返回值类型改写为派生类类型的指针
#include<iostream>
using namespace std;

class Animal{
public:
    int value;
    Animal():value(0){}
    Animal(int v):value(v){}
    virtual Animal* show(){ //返回值类型是Animal类型的指针 
        cout<<"Animal类中的value值是:"<<value<<endl;
        return this;
    } 
};

class Person:public Animal{ 
public:
    int value;
    Person():value(0){}
    Person(int v):value(v){}
    Person* show(){ //返回值类型是Person类型的指针  
        cout<<"Person类中的value值是:"<<value<<endl;
        return this; 
    }
};

int main(){
    Animal animal(10);
    Person person(20);
    animal.show();
    cout<<"------------分界线----------------"<<endl;
    person.show();
    return 0;
}

只有类中的成员函数才有资格成为虚函数。这是因为虚函数仅适用于有继承关系的类对象(建议成员函数尽可能地设置为虚函数)
类中的静态成员函数是该类所有对象所共有的,不受限于某个对象个体,因此不能作为虚函数

类中的析构函数可以作为虚函数,但构造函数不能作为虚函数。这是因为在调用构造函数时对象还没有完成实例化,而调用析构函数时对象已经完成了实例化
        
构造函数不能是虚函数,析构函数可以是虚函数且推荐最好设置为虚函数。
        
虚函数,在类成员方法的声明(不是定义)语句前加“virtual”, 如 virtual void func()
纯虚函数,在虚函数后加“=0”,如 virtual void func()=0
对于虚函数,子类可以(也可以不)重新定义基类的虚函数,该行为称之为复写Override。
对于纯虚函数,子类必须提供纯虚函数的个性化实现。
在派生子类中对虚函数和纯虚函数的个性化实现,都体现了“多态”特性。但区别是:

子类如果不提供虚函数的实现,将会自动调用基类的缺省虚函数实现,作为备选方案;
子类如果不提供纯虚函数的实现,编译将会失败。尽管在基类中可以给出纯虚函数的实现,但无法通过指向子类对象的基类类型指针来调用该纯虚函数,也即不能作为子类相应纯虚函数的备选方案。
(纯虚函数在基类中的实现跟多态性无关,它只是提供了一种语法上的便利,在变化多端的应用场景中留有后路。)

=========================================================================

1.什么是纯虚函数
定义纯虚函数就是为了让基类不可实例化化


=========================================================================
1.内联函数的由来
当我们调用函数的时候,实际会有额外的开销,为了避免或减少这些额外的开销,c++中引入内联函数(inline functions)。

2.内联函数的原理
1)当我们调用内联函数时,不会像调用普通函数那样额外开销,而是把内联函数的代码直接嵌入到调用它的地方去,但仍然保持其独立性。

2)如果一个函数为内联函数,它就不会出现在最终的可执行代码里,只是会存在于编译器中,在编译器需要的地方插入。

3.内联函数的写法
在一般函数前面加上inline的关键字,在头文件里定义而非声明。

4.内联函数的优点
1)以牺牲代码的空间换时间,提高了效率。

2)与c语言的宏的思想类似,但要优于宏,因为宏不能做类型检查,而内联函数作为一个函数可以进行类型检查。

5.被自动默认的内联函数
1)在class声明中定义了成员函数,这些函数都是内联的。

2)如果我们为了使类看起来直观,在class中声明了成员函数,也可在class下方定义,将成员函数变为内联函数:

class a
{
private:
    int i;
public:
    a();
    void f1();
};
 
inline a::a() 
{
...
}
inline void a::f1()
{
...
}
6.使用内联函数与否的情况
建议使用的情况:
1)函数代码本身较短,系统可能默认处理其为内联函数

2)频繁被调用的,处于循环中的函数

不能使用的情况:
1)过于巨大的函数,编译器可能拒绝该函数作为内联函数来插入

2)递归函数

-------------------------------------------------------------

1、内联函数的诞生
为什么C++里面会引入内联呢?
答:为了解决频繁短小函数的调用,此时会使用内联函数。
大家回想一下C语言里面怎样解决短小函数频繁调用的?
答:在C里面我们是利用宏来解决短小函数频繁调用的。

说到这里,大家可以自己尝试写一个实现宏函数吧!
例:(求和的宏函数定义)

//#define ADD(x, y) return x + y;
//#define ADD(x, y) (x + y);
//#define ADD(x, y) x + y
//以上这都是错误写法,宏函数很容易写错
//宏还不支持调试

//验证自己宏写的是否正确,就是去替换验证一下
#define ADD(x, y) ((x)+(y))   //正确写法
哈哈哈~我相信以上宏写法的这些错误,肯定有不少人犯错吧。瞧瞧这就是宏,不但写的时候复杂容易出错之外,还晦涩难懂,不易理解并且也不支持调试。

因此C++就进行改进完善,引出了内联函数,来解决宏函数晦涩难懂,容易写错的问题。

2、内联函数概念
在函数声明或者定义的时候,以关键字inline修饰的函数就叫做内联函数。
例:(求两数之和的函数inline())

#include <iostream>
using namespace std;
inline int Add(int x = 10, int y = 8)//inline修饰的Add函数
{
    return x + y;
}
int main()
{
    int ret1 = Add(8, 24);
    int ret2 = Add(2, 4);
    int ret3 = Add(5, 6);
    cout << ret1 << " " << ret2 << " " << ret3 << endl;
    return 0;
}
运行结果
在这里插入图片描述

3、内联函数的特性
.1、inline是一种以空间换时间的做法,省去调用函数额外开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。

内联函数的声明、定义和调用方法与普通函数相同,但是C++里面对他们的处理方式不同。如果一个函数被定义为内联函数,在编译的时候,C++将用内联函数的代码代替对它这个函数的每次调用。
上面main函数里面多次调用内联函数Add(),C++编译器在编译此程序的时候,会对main函数中的函数做如下处理:

int main()
{
    int ret1 = 8 + 24;
    int ret2 = 2 + 4;
    int ret3 = 5 + 6;
    cout << ret1 << " " << ret2 << " " << ret3 << endl;
    return 0;
}
这里就可以看出,在需要调用Add()函数的时候,没有去调用Add()函数,而是直接使用Add()函数中的代码逻辑实现。


2. inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。

3. inline函数的声明或定义必须在函数的调用之前完成。

例:

#include <iostream>
using namespace std;

inline int Add(int x = 10, int y = 8);//声明Add是内联函数

int Add(int x, int y)//可以在这里前面加上inline,也可以省略(声明的时候添加了),意义都相同
{
    return x + y;
}
int main()
{
    int ret = 0;
    ret = Add();
    cout << ret << endl;
    return 0;
}
分析:
该程序中,Add函数在声明的时候冠以关键字inline,而定义的时候没有冠以关键字inline,但仍是内联函数。
错误示例:

#include <iostream>
using namespace std;

int Add(int x = 10, int y = 8);//声明不给inline,Add是一个普通函数
int main()
{
    int ret = 0;
    ret = Add();
    cout << ret << endl;
    return 0;
}
inline int Add(int x, int y)//定义的时候给inline
{
    return x + y;
}

分析:
这里Add()函数在声明的时候没有给关键字inline,即声明Add()是一个普通函数,
此时在main函数里面调用Add()时候,编译器就会先去找他的声明,根据声明确定它为一个普通函数,
因此,尽管在定义的时候有inline但是,此时编译器也无法识别。


========================================================================


. 和 ->

在C++编程中,"."和"->"是两个常用的运算符,用于访问对象的成员。虽然它们在功能上非常相似,但在使用方法和上下文中却有着明显的差异。本文将详细解析这两个运算符的使用和区别。


1."."运算符
"."运算符在C++中被称为直接成员访问运算符。它用于直接访问类或结构体的成员。这些成员可以是变量,也可以是函数。下面是一段简单的代码示例:

复制
class MyClass {
public:
    int myVariable;
    void myFunction() { /* 函数内容 */ }
};

MyClass obj;
obj.myVariable = 10;
obj.myFunction();

在上述代码中,我们定义了一个名为MyClass的类,该类有一个公共变量myVariable和一个公共函数myFunction。然后,我们创建了一个名为obj的MyClass对象,并使用"."运算符访问并修改myVariable的值,以及调用myFunction函数。

2. "->"运算符
"->"运算符在C++中被称为间接成员访问运算符。它用于通过指针访问类或结构体的成员。当我们有一个指向对象的指针而不是对象本身时,就需要使用"->"运算符。下面是一段简单的代码示例:

复制
class MyClass {
public:
    int myVariable;
    void myFunction() { /* 函数内容 */ }
};

MyClass* objPtr = new MyClass;
objPtr->myVariable = 20;
objPtr->myFunction();

在上述代码中,我们创建了一个指向MyClass对象的指针objPtr,并使用"->"运算符访问并修改myVariable的值,以及调用myFunction函数。

3. "."与"->"的区别
"."和"->"的主要区别在于它们所操作的对象类型:

"."运算符用于直接操作对象,也就是说,在对象上使用"."运算符可以直接访问其成员。
"->"运算符则用于通过指针操作对象。当我们有一个指向对象的指针而不是对象本身时,就需要使用"->"运算符。
尽管"."和"->"在功能上看起来非常相似,但由于C++语言的特性,它们在使用时有着明显的区别。在C++中,对象和指针是两种完全不同的数据类型,因此对于这两种类型的操作,我们需要使用不同的运算符。

4. 总结
总的来说,"."和"->"是C++中两个非常重要的运算符,用于访问类或结构体的成员。虽然它们在功能上非常相似,但在使用方法和上下文中却有着明显的差异。理解这些差异对于编写正确和有效的C++代码至关重要。


=========================================================================

c++ 中的入参和出参


一、基本概念
C++ 中的参数,既可以当做传入参数,也可以当做传出参数,具体要看参数的数据类型。

非指针,非引用类型:一定是当做传入参数;
指针,引用类型:既可以当做传入参数;也可以当做传出参数;甚至可以同时是传入参数,也是传出参数。
其次要看对参数进行的操作:

如果是只有读取,没有修改的,那就是传入参数,这时可以加上 const 限定符,以防不小心修改了参数。
如果只有对参数进行写入,没有读取的,那就是当做传出参数的。
如果对参数先进行了读取操作,然后又进行了写入操作,那么就是既当传入参数也当传出参数了。
 
二、示例
int func(int num1, int *num2)
{
    *num2 = 5;
    return num1 + *num2; 
}
其中,num1是传入参数,num2为传出参数。

传入参数本身有值,传入函数让函数使用;   传出参数本身没值,从函数中带出值(相当于函数的返回值)。


=========================================================================

使用 uint32_t 这种类型的时候需要引入头文件#include <stdint.h>

=========================================================================

函数默认值,默认值要在函数声明中设置,不能只在函数定义中设置,而且函数设置中和函数定义中设置的默认值不能一样

=========================================================================

String8 


网站公告

今日签到

点亮在社区的每一天
去签到