C++高频知识点(三)

发布于:2025-07-08 ⋅ 阅读:(13) ⋅ 点赞:(0)

11. 请解释const关键字在C++中的作用

const 关键字在 C++ 中有多种用途,主要用于定义常量或确保某些变量的值不被修改。以下是 const 在 C++ 中的一些主要用途:

  1. 定义常量:const 可以用来定义常量,即其值在程序运行过程中不可改变的量。例如:
const int a = 10;

这里,a 是一个常量,其值被设定为 10,并且不能在程序运行过程中被修改。

  1. 指针与 const:const 在指针的使用中特别有用,可以用它来保证指针指向的内容不被修改,或者保证指针本身的值(即内存地址)不被修改,或者两者都不被修改。
  • 指向常量的指针(指针指向的内容不能被修改)
const int *p = &a;
  • 常量指针(指针本身的值不能被修改)const的右边 离谁最近 就是修饰谁
    这里,p 是一个指针,它指向一个常量整数。你不能通过 p 来修改它所指向的值。
int b = 20;  
int *const q = &b;

这里,q 是一个常量指针,指向一个整数。你不能改变 q 的值(即它不能指向其他地址),但可以通过 q 来修改它所指向的内容。

  1. 函数参数与 const:在函数参数中使用 const 可以保证传递给函数的参数在函数体内不会被修改,从而增强代码的可读性和安全性。例如:
int CalculateSomething(const int &a, const int& b) {  
    // ... do something ...  
}

在这个函数中,a 和 b 的值都不能被修改。

  1. const 成员函数:在类的成员函数声明后面添加 const 关键字,表示这个成员函数不会修改类的任何成员变量(除了被声明为 mutable 的成员)。这有助于确保成员函数不会意外地修改对象的状态。例如:
class MyClass {  
public:  
    int GetValue() const {  
        return value_;  
    }  
private:  
    int value_;  
};

在这个例子中,GetValue 函数是一个常量成员函数,它保证不会修改 MyClass 的任何成员变量。

  1. constexpr:C++11 引入了 constexpr 关键字,它用于在编译时计算常量表达式的值。这与 const 相似,但 constexpr 强调表达式的值在编译时就是已知的。例如:
constexpr int x = 2 * 3; // x 在编译时就被计算为 6

12. 解释浅拷贝和深拷贝,并提供相应代码示例。

在C++中,浅拷贝和深拷贝是关于对象复制的重要概念。
浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果字段是基本数据类型,那么对该字段执行逐位拷贝。如果字段是引用类型(例如指针或引用),则拷贝引用但不拷贝引用的对象。因此,原始对象及其副本引用同一个对象。
深拷贝:将对象复制到另一个对象中,同时递归地复制对象中的引用类型的属性。这意味着,如果原对象内部的子对象发生变化,不会影响到已经复制出去的对象。
下面,我们分别用代码示例来解释浅拷贝和深拷贝:

浅拷贝示例:

#include <iostream>  
#include <bits/stdc++.h>

class StringShallowCopy {
public:
    char* str;

    // 构造函数  
    StringShallowCopy(const char* s = "") {
        if(s) {
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        } else {
            str = nullptr;
        }
    }

    // 拷贝构造函数 - 浅拷贝  
    StringShallowCopy(const StringShallowCopy& other) {
        // 注意,这里只是简单地拷贝了指针,没有拷贝指针指向的内容  
        str = other.str;
    }

    ~StringShallowCopy() {
        delete[] str;
    }

    void print() {
        std::cout<< str<< std::endl;
    }
};

int main() {
    StringShallowCopy s1{"Hello"};
    // 使用拷贝构造函数进行浅拷贝
    StringShallowCopy s2{s1};

    s2.print();
    // 这里会出现问题,因为s1和s2的str指向同一块内存,当s1析构时会释放这块内存,  
    // 然后s2析构时会再次尝试释放同一块内存,导致未定义行为(通常是程序崩溃)
    return 0;
}

在这里插入图片描述
注意:上面的浅拷贝示例中,析构函数会导致问题,因为两个对象的str成员指向同一块内存。在实际编程中,应避免这种情况。

深拷贝示例:

#include <iostream>  
#include <cstring>  

class StringDeepCopy {
public:
    char* str;

    // 构造函数
    StringDeepCopy(const char* s = "") {
        if(s) {
            str = new char[strlen(s) + 1];
            strcpy(str, s);
        } else {
            str = nullptr;
        }
    }

    // 拷贝构造函数 - 深拷贝
    StringDeepCopy(const StringDeepCopy& other) {
        if(this != &other) { // 防止自我赋值
            if(other.str) {
                str = new char[strlen(other.str) + 1];
                strcpy(str, other.str);
            } else {
                str = nullptr;
            }
        }
    }

    ~StringDeepCopy() {
        delete[] str; // 释放内存
    }   

    void print() {
        std::cout << str<< std::endl;
    }
};

int main() {
    StringDeepCopy s1{"Hello"};
    StringDeepCopy s2{s1}; // 调用拷贝构造函数进行深拷贝
    s2.print();
    // 这里没有问题,因为s1和s2的str指向不同的内存块
    return 0;
}

在这里插入图片描述

在上面的深拷贝示例中,StringDeepCopy类的拷贝构造函数为str成员分配了新的内存,并将原始字符串的内容复制到新内存中。这样,每个对象都有自己的str副本,互不影响。

13. 解释指针与数组之间的关系,如何通过指针遍历数组?

指针和数组在C/C++语言中有着紧密的关系。简单来说,数组名可以被看作是指向数组第一个元素的指针。这种关系使得我们可以通过指针来访问和遍历数组。

在这里插入图片描述

#include <stdio.h>  
  
int main() {  
    int arr[] = {1, 2, 3, 4, 5};  
    int *p = arr;  // 指针p指向数组的第一个元素  
    int i;  
  
    for (i = 0; i < 5; i++) {  
        printf("%d ", *(p + i));  // 使用指针算术来访问数组元素  
    }  
    printf("\n");  
  
    // 或者,你也可以这样遍历数组:  
    for (p = arr; p < arr + 5; p++) {  
        printf("%d ", *p);  // 通过解引用指针p来访问当前指向的元素  
    }  
    printf("\n");  
  
    return 0;  
}

在这里插入图片描述

14. 解释auto关键字在C++11中的作用及其使用场景。

在C++11中,auto关键字被引入用于自动类型推导,即让编译器根据初始化表达式自动推导出变量的类型。这一特性极大地简化了代码书写,减少了类型声明的繁琐性,同时也增强了代码的可读性和可维护性。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

15. 什么是移动语义(Move Semantics)?它有什么优势?

在这里插入图片描述

下面是一个简单的代码样例,展示了如何使用std::move来移动一个std::string对象:

#include <iostream>  
#include <string>  
#include <utility> // 为了使用 std::move  
  
class MyString {
public:
    MyString(const std::string&s): data(new std::string(s)) {

    }

    // 移动构造函数
    MyString(MyString&& other) noexcept: data(other.data) {
        other.data = nullptr; // 将源对象的指针置为nullptr,避免析构时释放内存
    }

    // 移动赋值运算符
    MyString& operator=(MyString&& other) noexcept {
        if(this != &other) {
            delete data; // 释放当前对象的内存
            data = other.data; // 转移资源
            other.data = nullptr; // 将源对象的指针置为nullptr,避免析构时释放内存
        }
        return *this;
    }

    // 析构函数
    ~MyString() {
        delete data; // 释放内存
    }

    std::string& getdata() const{
        return *data; // 返回字符串数据
    }

private:
    std::string* data; // 使用指针来管理字符串数据
};

int main() {
    MyString s1("Hello, World!");
    MyString s2 = std::move(s1); // 使用移动赋值运算符转移资源

    // 此时s1的资源已经被移动到s2,所以s1现在是一个空对象(或无效对象)  
    // 尝试访问s1可能会导致未定义行为  
  
    // 注意:在实际应用中,你应该避免在移动后使用s1,除非你确定它是安全的  
  
    // 输出s2以验证资源已经被移动  
    std::cout<< s2.getdata()<< std::endl;

    return 0;
}

在这里插入图片描述

在这里插入图片描述

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!


网站公告

今日签到

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