C++基础

引用占用内存空间吗?

引用是占用内存空间的,而且其占用的内存和指针一样,因为引用的内部实现就是通过指针来完成的。

C/C++内存有哪几种类型?

C中,内存分为5个区:堆(malloc)、栈(如局部变量、函数参数)、程序代码区(存放二进制代码)、全局/静态存储区(全局变量、static变量)和常量存储区(常量)。此外,C++中有自由存储区(new)一说。
全局变量、static变量会初始化为缺省值,而堆和栈上的变量是随机的,不确定的。

堆和栈的区别?

1).堆存放动态分配的对象——即那些在程序运行时动态分配的对象,比如 new 出来的对象,其生存期由程序控制;
2).栈用来保存定义在函数内的非static对象,如局部变量,仅在其定义的程序块运行时才存在;
3).静态内存用来保存static对象,类static数据成员以及定义在任何函数外部的变量,static对象在使用之前分配,程序结束时销毁;
4).栈和静态内存的对象由编译器自动创建和销毁

左值和右值

  • 可以取地址的,有名字的,非临时的就是左值
  • 不能取地址的,没有名字的,临时的,通常生命周期就在某个表达式之内的就是右值

在C++中,左值(lvalue)和右值(rvalue)引用是C++11引入的一个重要特性,对资源的管理和性能优化有很大的影响。了解它们的区别和作用对于写出高效且优雅的代码非常重要。

左值(lvalue)和右值(rvalue)的定义

  1. 左值(lvalue)

    • 左值是指一个具有持久性(可以存储在内存中的某个位置)的对象,可以被取地址(使用&运算符)。
    • 左值可以出现在赋值操作的左侧。
    • 示例:变量、数组元素、解引用的指针等。
    int a = 10;      // a是左值
    int* p = &a;    // 可以取a的地址
    
  2. 右值(rvalue)

    • 右值是指一个临时对象,通常在表达式中用作值,不具有持久性,无法被取地址。
    • 右值一般是表达式的结果,如算术运算、函数返回值等。
    • 示例:字面值、临时变量、某些函数返回的非引用值。
    int b = a + 5;  // (a + 5)是一个右值
    

左值引用(lvalue reference)和右值引用(rvalue reference)

  1. 左值引用

    • 使用符号&声明,能够绑定到左值。
    • 允许对被引用对象进行修改。
    int a = 10;
    int& ref = a;  // ref是左值引用,指向a
    ref = 20;      // a现在变为20
    
  2. 右值引用

    • 使用符号&&声明,能够绑定到右值。
    • 允许资源的移动,而非复制,提高性能。
    int&& rref = 30;  // rref是右值引用,绑定到右值30
    

区别与作用

  • 绑定对象

    • 左值引用可以绑定到左值,右值引用可以绑定到右值。
  • 用途

    • 左值引用常用于普通变量的引用。
    • 右值引用主要用于实现移动语义(move semantics)和完美转发(perfect forwarding)。利用右值引用,可以在不复制对象的情况下转移对象的资源。
  • 移动语义

    • 通过右值引用,可以实现移动构造函数和移动赋值运算符,从而提高性能,特别是在处理大量动态分配内存的对象时。
    class MyClass {
    public:
        MyClass() { /* 构造 */ }
        MyClass(const MyClass& other) { /* 复制构造 */ }
        MyClass(MyClass&& other) noexcept { /* 移动构造 */ }
        MyClass& operator=(MyClass&& other) noexcept { /* 移动赋值 */ }
    };
    
  • 完美转发

    • 当函数接收参数并可能将其传递给其他函数时,使用右值引用可以通过std::forward实现完美转发。

示例

#include <iostream>
#include <utility> // for std::move

class Resource {
public:
    Resource() { std::cout << "Resource acquired\n"; }
    Resource(const Resource&) { std::cout << "Resource copied\n"; }
    Resource(Resource&&) noexcept { std::cout << "Resource moved\n"; }
    ~Resource() { std::cout << "Resource released\n"; }
};

void useResource(Resource res) { /* 使用资源 */ }

// 通过右值引用,实现移动语义
void createAndUseResource() {
    Resource r; // 资源构造
    useResource(std::move(r)); // 移动而不是复制
}

int main() {
    createAndUseResource();
    return 0;
}
  • 左值和右值的引入,使得C++能够在管理资源和性能方面更为灵活。
  • 左值引用和右值引用的区分,允许开发者更好地控制内存使用,减少不必要的复制,提高程序性能。

什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?

用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。

1). 使用的时候要记得指针的长度.
2). malloc的时候得确定在那里free.
3). 对指针赋值的时候应该注意被赋值指针需要不需要释放.
4). 动态分配内存的指针最好不要再次赋值.
5). 在C++中应该优先考虑使用智能指针.

C++中的设计模式有哪些?

常见的设计模式包括但不限于:
创建型模式:单例模式、工厂模式、建造者模式。
结构型模式:适配器模式、装饰器模式、外观模式。
行为型模式:观察者模式、策略模式、命令模式。
每个设计模式都有其特定的用途和实现方式。

C++为什么要加extern“C”

在C++中使用extern "C"的主要原因是为了控制C和C++之间的名称修饰(name mangling)行为。当你在C++代码中包含C语言的头文件或库时,使用extern "C"可以确保以C语言编写的函数以C语言的方式进行链接,而不会被C++的名称修饰机制修改。

C++中指针和引用的区别是什么?

答案

  • 指针:可以指向不同的对象,支持算术运算,可以为空,语法上使用*&
  • 引用:必须在初始化时绑定到一个对象,不能指向NULL,不能被改变为指向其他对象,更像是对象的别名。

解释一下C++中的虚函数和纯虚函数。

答案

  • 虚函数:在基类中使用virtual关键字声明,允许在派生类中重写(override),实现多态性。
  • 纯虚函数:在基类中声明为virtual void func() = 0;,表示该函数没有实现,强制派生类必须实现它,从而使得基类成为抽象类。

讲解一下RAII是什么?

答案
RAII(资源获取即初始化)是一种通过对象生命周期管理资源(如动态内存、文件句柄、网络连接等)的编程技术。在对象构造时获取资源,在析构时释放资源,避免了资源泄露和显式释放的不便。

C++中如何实现单例模式?

答案
可以通过以下方法实现单例模式:

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance; // 懒汉式
        return instance;
    }

private:
    Singleton() {} // 私有构造函数
    Singleton(const Singleton&) = delete; // 删除拷贝构造函数
    void operator=(const Singleton&) = delete; // 删除赋值运算符
};

什么是深拷贝和浅拷贝?

答案

  • 浅拷贝:复制对象的基本类型成员(值)和指针成员(地址),多个对象指向相同的内存。
  • 深拷贝:为所有指针成员分配新的内存,并复制所指向的内容,确保不同对象有独立的内存。

C++11中的auto关键字的作用是什么?

答案
auto关键字用于自动推导变量的类型,编译器根据初始化表达式来确定变量的类型,增加代码的简洁性和可读性。

auto x = 5; // x的类型为int
auto y = 3.14; // y的类型为double

什么是模板(Template)?

答案
模板是C++的一种通用编程机制,允许在编写代码时使用类型参数。通过模板,可以实现类型安全的泛型函数和类,例如:

template <typename T>
T add(T a, T b) {
    return a + b;
}

类的构造函数和析构函数有什么作用?

答案

  • 构造函数:在创建对象时自动调用,用于初始化对象的状态和分配资源。
  • 析构函数:在对象生命周期结束时自动调用,用于释放资源和执行清理操作。

C++中的多重继承有什么潜在问题?

答案

  • 菱形继承:当多个基类有共同的基类时,派生类可能会含有多个相同基类的实例,造成二义性。
  • 复杂性增加:多重继承会增加代码的复杂性和维护难度。

解释一下内存管理中的newdelete

答案

  • new用于动态分配内存,返回指向新分配内存的指针。
  • delete用于释放之前通过new分配的内存。对数组使用delete[]

C++中的深拷贝和浅拷贝如何实现?

答案

  • 浅拷贝:编译器默认提供。对对象进行简单的赋值,会复制基本类型成员和指针,但指针指向同一内存。

    class Example {
        int* data;
    public:
        Example(int value) {
            data = new int(value);
        }
    };
    
    Example obj1(5);
    Example obj2 = obj1; // 浅拷贝
    
  • 深拷贝:需手动实现,通过复制指针所指向的内容来分配新的内存。

    class Example {
        int* data;
    public:
        Example(int value) {
            data = new int(value);
        }
        
        // 实现深拷贝构造函数
        Example(const Example& obj) {
            data = new int(*obj.data); // 分配新内存
        }
        
        ~Example() {
            delete data; // 释放内存
        }
    };
    

C++中的nullptr是什么,为什么用它?

答案
nullptr是C++11引入的新关键字,表示空指针。与老式的NULL相比,nullptr具有类型安全性,可以避免类型转换错误。例如,nullptr可以赋给任何指针类型。

int* ptr = nullptr; // 安全表示空指针

C++支持哪些类型的多态?

答案
C++支持以下类型的多态:

  • 编译时多态(静态多态):
    • 函数重载
    • 运算符重载
  • 运行时多态(动态多态):
    • 通过虚函数实现的多态。

解释一下C++中的智能指针。

答案
智能指针是用来替代原始指针的类,以自动管理对象的生命周期。C++标准库提供了几种智能指针:

  • std::unique_ptr:独占所有权,不能复制,但可以移动(move)。
  • std::shared_ptr:共享所有权,多个指针可以指向同一个对象,引用计数管理。
  • std::weak_ptr:与shared_ptr配合使用,避免循环引用,不增加引用计数。

C++中的const关键字的用法是什么?

答案
const关键字用于定义常量,可以用于修饰基本数据类型、指针和成员函数:

  • 基本数据类型:定义常量值。

    const int x = 10; // 常量整型
    
  • 指针:表示指针指向的内容不可更改。

    const int* p; // 指针指向内容不可变
    
  • 成员函数:表示该函数不会修改类的成员变量。

    class Example {
    public:
        void display() const; // 该函数不会修改对象状态
    };
    

解释一下C++中的异常处理机制。

答案
C++通过trycatchthrow关键字进行异常处理:

  • throw:用于抛出异常。
  • try:用于定义可能抛出异常的代码块。
  • catch:用于捕获异常,并处理。
try {
    throw std::runtime_error("An error occurred");
} catch (const std::runtime_error& e) {
    std::cout << e.what() << std::endl; // 处理异常
}

什么是static关键字的作用?

答案

  • 在函数内:用来定义局部静态变量,生命周期从声明时开始,直到程序结束。
  • 在类中:定义静态成员变量/函数,属于类而不是对象;所有对象共享该数据。
  • 在全局作用域:限制变量或函数的链接属性,在文件内可见。

C++中的友元(friend)是什么?

答案
友元是一种特殊的访问权限修饰符,可以让指定的函数或类访问另一个类的私有和保护成员。友元关系不是继承关系,也不是典型的封装关系。

class Example {
    friend void show(Example& obj);
private:
    int data;
};

std::vector的优缺点是什么?

答案

  • 优点

    • 动态数组,能够自动调整大小。
    • 提供随机访问,支持快速元素插入和删除(在结尾)。
  • 缺点

    • 在中间或前面插入和删除元素性能较差(需要移动元素)。
    • 包含额外的内存开销(分配和管理内存)。

C++中的enumenum class有何不同?

答案

  • enum(枚举):

    • 传统枚举,属于全局作用域,成员之间存在隐式转换。
  • enum class(强枚举):

    • C++11引入,作用域有限,枚举成员在其枚举类型下,提供类型安全,不允许隐式转换。
enum Color { Red, Green, Blue }; // 全局作用域
enum class Status { Ok, Error }; // 强枚举

当然可以!以下是更多的C++常见面试题及其答案:

C++中什么是const关键字?

答案
const关键字用于声明常量,即声明的变量在初始化后不可更改。它可以用于变量、指针、类成员函数等。

  • 对于变量:

    const int a = 10; // a不可修改
    
  • 对于指针:

    const int* p; // p指向的值不可修改
    int* const q; // q的地址不可修改
    const int* const r; // r的地址和指向的值都不可修改
    
  • 对于成员函数:

    class MyClass {
    public:
        void show() const { // 此函数不会修改类的成员变量
        }
    };
    

C++中什么是命名空间(namespace)?

答案
命名空间用于解决不同代码库之间的命名冲突。通过将相关的标识符(如变量、函数等)封装在命名空间中,可以避免同名冲突。

namespace MyNamespace {
    void func() {
        // function code
    }
}

// 使用命名空间
MyNamespace::func();

C++中什么是RAII(资源获取即初始化)?

答案
RAII是一种编程惯用法,意味着资源(如内存、文件句柄等)的获取在对象的构造时完成,资源的释放在对象的析构时完成。这样,资源管理自动化,避免了内存泄漏。

class Resource {
public:
    Resource() {
        // 获取资源
    }
    ~Resource() {
        // 释放资源
    }
};

// 使用RAII
void func() {
    Resource res; // 自动管理
}

C++中如何进行类型转换?

答案
C++支持几种类型转换,使用关键字可以确保类型安全:

  • static_cast:用于基本类型之间的转换。
  • dynamic_cast:用于安全地将基类指针转换为派生类指针,通常与虚函数结合使用。
  • const_cast:用于在常量和非常量之间转换。
  • reinterpret_cast:用于强制转换类型,通常不安全。
int a = 10;
double b = static_cast<double>(a); // 静态转换

Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // 动态转换

const int* c = new int(5);
int* d = const_cast<int*>(c); // 常量转换

C++中的随机数生成如何进行?

答案
C++11引入了更强大的随机数生成库,包括不同的随机数引擎和分布。常用的方法是使用 <random> 头文件。

#include <random>

std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1, 100);

int random_num = distribution(generator); // 生成1到100之间的随机数

C++中什么是移动语义?

答案
移动语义是C++11引入的一项重要特性,通过使用“移动构造函数”和“移动赋值运算符”,允许将资源的所有权从一个对象转移到另一个对象,而不是通过复制。有助于提高性能,特别是在处理动态分配的资源时。

class MyClass {
public:
    MyClass(MyClass&& other) noexcept { // 移动构造函数
        data = other.data;
        other.data = nullptr;
    }
    
    MyClass& operator=(MyClass&& other) noexcept { // 移动赋值
        if (this != &other) {
            delete data; // 释放旧资源
            data = other.data;
            other.data = nullptr;
        }
        return *this;
    }
};

C++中的设计模式有哪些?

答案
常见的设计模式包括但不限于:

  • 创建型模式:单例模式、工厂模式、建造者模式。
  • 结构型模式:适配器模式、装饰器模式、外观模式。
  • 行为型模式:观察者模式、策略模式、命令模式。

每个设计模式都有其特定的用途和实现方式。

C++中什么是函数重载和运算符重载?

答案

  • 函数重载是指在同一作用域内,可以定义多个同名的函数,这些函数可以有不同的参数类型和数量。

    void func(int a);
    void func(double b);
    
  • 运算符重载是指用户可以为自定义类型定义运算符的行为,使得可以使用运算符对其进行操作。

    class Complex {
    public:
        double real, imag;
        Complex operator+(const Complex& other) {
            return Complex(real + other.real, imag + other.imag);
        }
    };
    

C++中的多重继承和菱形继承是什么?

答案

  • 多重继承:一个类可以继承多个基类。例如:class Derived : public Base1, public Base2 {}。可以导致复杂性和二义性问题。
  • 菱形继承(钻石问题):当一个类A被类B和类C继承,同时类B和类C又被类D继承,可能导致D类不确定调用哪个A类的成员。

解决菱形继承的方式是使用虚继承:

class A { };
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { };

C++中的标准模板库(STL)是什么?

答案
STL是一个广泛使用的C++库,提供了一组通用的类和算法,用于数据结构(如向量、列表、队列、堆栈、集合等)和算法(如排序、查找等)。STL使得C++程序员能够快速构建高效的程序。

C++中如何使用析构函数防止内存泄漏?

答案
析构函数可以释放在类中分配的动态内存,以避免内存泄漏。通过在析构函数中删除指针,可以确保当对象生命周期结束时,被占用的内存被释放。

class MyClass {
public:
    int* data;
    
    MyClass() {
        data = new int[10]; // 动态分配内存
    }

    ~MyClass() {
        delete[] data; // 释放内存
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值