C++基础问题整理(一)

  1. 引用是否占用内存?

引用实际上是占用内存空间的,但是程序把它按照不占用内存空间来处理。

实际在内存空间上,引用本身也占用一块内存,里面存储着所引用的变量的地址,大小与指针相同。只是经过编译器处理后,访问这块内存时将直接转向访问其指向的内存,因此在程序中无法读取到这块内存本身。

  1. 函数参数使用引用的好处:可以避免在函数调用时进行大量数据拷贝

传递引用给函数,这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作

  • 使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

  • 使用指针作为函数的参数虽然也能达到使用引用的效果,但是在被调函数中同样要给形参分配存储单元,且需要重复使用“*指针变量名”的形式进行运算,容易产生错误且程序阅读性差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参,而引用更容易使用。

#include<iostream>
using namespace std;
void fun(int &a, int &b) {
    int c = a;
    a = b;
    b = c;
}
int main()
{
    int a,b;
    cin >> a >> b;
    fun(a, b);
    cout <<a<<" "<< b;
    return 0;
}
  1. 引用在拷贝构造中使用

调用拷贝构造函数时是实参向形参传值,如果传进来的不是引用,那么就是值传递,就会在函数里又重新创建一个对象,而重新创建对象又是通过拷贝构造函数,所以不是引用的话就会一直调用下去。

  1. 什么情况下会造成野指针,野指针有什么坏处,带来什么影响?

野指针是指指向未知内存地址的指针。

出现原因:

  • 局部指针变量没有被初始化。局部变量不像全局变量那样不赋值就会初始化为0,局部指针变量指向的内存空间地址是随机的,不能向随机地址空间写数据。

  • 释放内存后指针不及时置空,依然指向该内存,会出现非法访问的错误。

  • 使用已经释放过的指针

  • 指针操作超越变量作用域:

不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

有什么坏处:

野指针指向的位置是随机的, 危害也是随机的,不一定会产生错误。若程序产生错误,一般为内存泄露导致程序中断。严重的情况,若野指针指向的位置存放一个病毒,对其解引用后就会导致电脑中毒。因为指针变量在定义时如果未初始化,值也是随机的。

怎样避免:

  • 初始化置空

  • 申请内存后判空

  • 释放内存后判空

  • 使用智能指针:智能指针:shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。每次存取之前使用智能指针的方法_Expired进行指针的有效性检查,如果失败,则表明该对象已经被释放。

  1. 内存泄漏的几种情况

在堆区申请的内存没被释放掉。

  • 在类的构造函数和析构函数中没有匹配的调用new和delete:

  1. 在堆里创建了对象占用内存,但没有释放对象占用的内存。

  1. 在类的构造函数中动态的分配了内存,但是在析构函数中没有释放内存或者没有正确的释放内存

  • 缺少拷贝构造函数。类中默认的拷贝构造时浅拷贝的方式,一旦释放内存,就会导致一块内存被释放多次,产生内存泄漏。所以必须写出深拷贝方式的拷贝构造。

  • 没有将基类的析构函数定义为虚函数,当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数不会被调用,子类的资源没有正确释放,造成内存泄漏。

  1. 怎样避免内存泄漏

使用智能指针

为了解决动态内存的使用安全问题,C++ 才引入了智能指针的概念,智能指针除了具备普通内存的所有功能之外,还可以保证所指向的对象不再被被引用的时候,自动释放该对象。标准库提供的两种智能指针 shared_ptr 和 unique_ptr,二者的区别在于管理底层指针的方法不同,shared_ptr允许多个指针指向同一个对象,内部通过引用计数知道对象被几个指针引用,当引用为0的时候就是该对象将被释放的时候;unique_ptr 则“独占”所指向的对象,它不能被赋值,只能通过 std::move() 将引用转移到另一个 unique_ptr。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,作为观察者指向 shared_ptr 所管理的对象,不会改变对象的引用计数。这三种智能指针都定义在memory头文件中。 使用智能指针直接管理动态内存,在使用之后不需要手动释放,当这段内存不再被引用的时候,这段内存会被调用 free 函数来释放,free函数是作为自定义的释放函数传给智能指针的,如果是其他类型的对象,不需要传入释放函数,会默认调用类型的析构函数来释放。

  1. 什么情况下会重载

在有的情境下我们需要实现几个功能相似的函数,可能只有细节上有区别。比如求两个数的和,这两个数有多重类型,可能是int,float,double,char,bool等。如果在C语言中我们往往需要设计多个不同名称的函数。C++ 允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是函数的重载。借助重载,一个函数名可以有多种用途。

函数的重载的规则:

  • 函数名称必须相同。

  • 参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。

  • 函数的返回类型可以相同也可以不相同。

  1. C++如何做到函数重载

利用了一种name mangling (倾轧)技术,编译器会根据函数的参数列表,给每个函数生成一个唯一的标识符,这样就可以区分不同的同名函数了。name mangling 的规则是由编译器决定的,不同的编译器可能有不同的规则。而且,name mangling 只和函数名和参数列表有关,和返回值类型无关。所以仅以返回值类型不同不能构成函数重载。

  1. 参数默认值为什么不能跨越赋值

因为这样会造成编译器无法确定调用哪个参数的默认值。

// 错误的例子
void print(int x = 10, int y); // 第一个参数有默认值,第二个没有

// 函数调用
print(); // 编译器不知道是使用 x 的默认值还是 y 的默认值,报错
  1. 子类成员函数如何调用父类成员函数
  • 如果类B是类A的子类,则在类B的成员方法中调用类A的方法时,可以直接以A::method(paramlist); 来调用。

  • 若子类B重写了父类A的同名方法,则类A的实例调用的是类A的方法,类B的实例调用的是类B的方法;将类B实例的指针指向类A的指针变量ptr,则通过ptr调用的是类A的方法。

  • 若定义了类B的实例B1, 则通过B1调用类A的方法的用法为:B1.A::method(paramlist);

  • 若子类与父类方法同名,但是参数列表不一致,在子类中调用父类方法,由于重写规则,导致编译出错。此时可以使用using语句,使父类方法在子类中可见。

  1. 如果一个类中只有静态成员,类的大小为1。静态成员不计入大小,子类和父类会共享静态成员,不会被继承,因为共享用的一个。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值