首页 > 编程语言 >分享C++程序员面试八股文(十四)

分享C++程序员面试八股文(十四)

时间:2024-09-30 10:53:56浏览次数:3  
标签:std 八股文 int C++ 程序员 参数 shared ptr 模板

以下是 C++ 常见八股文(十四):

一、C++ 中的智能指针高级用法(Advanced Usage of Smart Pointers)

  1. 解释 unique_ptr、shared_ptr 和 weak_ptr 的循环引用问题及解决方法

    • 循环引用问题

      • 当使用shared_ptr进行相互引用时,可能会导致循环引用问题。例如,两个对象相互持有对方的shared_ptr,这会使得它们的引用计数永远不为零,即使没有外部引用指向它们,也不会被自动释放内存,从而造成内存泄漏。
    • 解决方法

      • 使用weak_ptr来打破循环引用。weak_ptr是一种弱引用,它不会增加对象的引用计数。当需要访问被weak_ptr指向的对象时,可以通过调用lock()方法来获取一个shared_ptr,如果对象仍然存在,则可以正常访问;如果对象已经被释放,则返回一个空的shared_ptr。例如:
        #include <iostream>
        #include <memory>
        
        class B;
        
        class A {
        public:
            std::shared_ptr<B> ptrB;
            ~A() {
                std::cout << "A destroyed." << std::endl;
            }
        };
        
        class B {
        public:
            std::shared_ptr<A> ptrA;
            ~B() {
                std::cout << "B destroyed." << std::endl;
            }
        };
        
        int main() {
            {
                std::shared_ptr<A> a = std::make_shared<A>();
                std::shared_ptr<B> b = std::make_shared<B>();
                a->ptrB = b;
                b->ptrA = a;
            }
            std::cout << "End of scope." << std::endl;
            return 0;
        }

        在这个例子中,由于AB相互持有对方的shared_ptr,导致它们在离开作用域时不会被自动释放。如果将其中一个改为weak_ptr,就可以解决这个问题:

        class B;
        
        class A {
        public:
            std::shared_ptr<B> ptrB;
            ~A() {
                std::cout << "A destroyed." << std::endl;
            }
        };
        
        class B {
        public:
            std::weak_ptr<A> ptrA;
            ~B() {
                std::cout << "B destroyed." << std::endl;
            }
        };
        
        int main() {
            {
                std::shared_ptr<A> a = std::make_shared<A>();
                std::shared_ptr<B> b = std::make_shared<B>();
                a->ptrB = b;
                b->ptrA = a;
            }
            std::cout << "End of scope." << std::endl;
            return 0;
        }

      • 如何自定义删除器(deleter)与智能指针结合使用?

        • 有时候需要为智能指针指定自定义的删除器,特别是在管理资源不是简单的delete操作时。例如,管理文件句柄、网络连接等资源时,可能需要进行特定的清理操作。
        • 对于unique_ptrshared_ptr,可以通过模板参数传递自定义删除器。例如:
          void customDeleter(int* ptr) {
              std::cout << "Custom deleter called." << std::endl;
              delete ptr;
          }
          
          int main() {
              std::unique_ptr<int, decltype(customDeleter*)> ptr(new int(10), customDeleter);
              return 0;
          }

          在这个例子中,为unique_ptr指定了一个自定义的删除器函数customDeleter。对于shared_ptr,也可以类似地传递自定义删除器:

          struct CustomDeleter {
              void operator()(int* ptr) const {
                  std::cout << "Custom deleter for shared_ptr called." << std::endl;
                  delete ptr;
              }
          };
          
          int main() {
              std::shared_ptr<int> ptr(new int(10), CustomDeleter());
              return 0;
          }

二、C++ 中的模板别名与模板参数推导(Template Aliases and Template Parameter Deduction)

  1. 解释模板别名(using)的作用及使用场景

    • 作用

      • 模板别名可以为复杂的模板类型提供一个更简洁易读的名称。它允许程序员为模板类型定义一个新的名称,使得代码更加清晰和易于维护。
    • 使用场景

      • 简化复杂的模板类型:当模板类型非常复杂时,使用模板别名可以使代码更易于理解。例如,对于一个包含多个模板参数的容器类型,可以定义一个模板别名来简化其使用。
      • 提高代码的可维护性:如果模板类型的定义发生变化,只需要修改模板别名的定义,而不需要在整个代码中进行大量的修改。
      • 增强代码的可读性:模板别名可以为特定的模板类型赋予一个有意义的名称,提高代码的可读性。例如,可以为一个特定的模板类型定义一个别名,如MyContainerType,使得代码更易于理解其用途。
    • 例如:

      template <typename T>
      using MyContainer = std::vector<T>;
      
      int main() {
          MyContainer<int> vec;
          return 0;
      }

    • 深入探讨模板参数推导在复杂模板场景下的工作原理

      • 在复杂模板场景下,模板参数推导可能会变得更加复杂。例如,当模板函数接受多个模板参数,或者模板参数是另一个模板类型时,编译器需要根据函数调用的实参来推导模板参数。
      • 对于函数模板,编译器会根据实参的类型来推导模板参数。如果实参的类型与模板参数的类型不完全匹配,编译器会尝试进行类型转换,以找到最适合的模板参数。例如:
        template <typename T, typename U>
        void func(T t, U u) {
            // 函数体
        }
        
        int main() {
            func(10, 20.5);
            return 0;
        }

        在这个例子中,编译器会根据实参1020.5的类型推导出模板参数TintUdouble

      • 对于类模板,模板参数推导通常发生在类模板的构造函数中。如果构造函数的参数类型与类模板的参数类型不完全匹配,编译器也会尝试进行类型转换,以找到最适合的模板参数。例如:
        template <typename T>
        class MyClass {
        public:
            MyClass(T t) {
                // 构造函数体
            }
        };
        
        int main() {
            MyClass obj(10);
            return 0;
        }

        在这个例子中,编译器会根据实参10的类型推导出模板参数Tint

三、C++ 中的移动语义与完美转发(Move Semantics and Perfect Forwarding)

  1. 深入理解移动语义在复杂对象中的应用

    • 移动语义不仅适用于简单类型,还可以在复杂对象中发挥重要作用。例如,对于包含动态分配内存的类、容器类等,移动语义可以大大提高性能。
    • 当一个对象被移动时,资源的所有权从源对象转移到目标对象,源对象通常被设置为一个有效但可确定的状态。例如,对于一个包含动态分配数组的类:
      class MyComplexClass {
      private:
          int* data;
      public:
          MyComplexClass() : data(new int[10]) {}
          ~MyComplexClass() {
              delete[] data;
          }
          MyComplexClass(MyComplexClass&& other) noexcept : data(other.data) {
              other.data = nullptr;
          }
          MyComplexClass& operator=(MyComplexClass&& other) noexcept {
              if (this!= &other) {
                  delete[] data;
                  data = other.data;
                  other.data = nullptr;
              }
              return *this;
          }
      };

      在这个例子中,移动构造函数和移动赋值运算符实现了资源的转移,避免了不必要的内存复制。

    • 解释完美转发的概念及在函数模板中的实现

      • 概念

        • 完美转发是指在函数模板中,将参数原封不动地转发给另一个函数,保持参数的左值 / 右值属性和 const/volatile 修饰符。这样可以确保函数模板能够正确地将参数转发给其他函数,而不会丢失参数的原始属性。
      • 实现

        • 在 C++ 中,可以使用std::forward来实现完美转发。例如:
          template <typename T>
          void func(T&& arg) {
              anotherFunc(std::forward<T>(arg));
          }

          在这个例子中,函数模板func接受一个参数arg,并使用std::forward将其转发给另一个函数anotherFunc,实现了完美转发。

四、C++ 中的常量表达式与编译期计算(Constant Expressions and Compile-Time Computation)

  1. 介绍常量表达式在 C++ 中的重要性及使用场景

    • 重要性

      • 常量表达式在 C++ 中具有重要的作用,它可以在编译期进行计算,避免了运行时的开销。常量表达式可以用于定义数组大小、初始化静态成员变量、作为模板参数等。
    • 使用场景

      • 数组大小定义:可以使用常量表达式来定义数组的大小,这样可以在编译期确定数组的大小,避免了动态分配内存的开销。例如:
        constexpr int size = 10;
        int arr[size];

      • 静态成员变量初始化:可以使用常量表达式来初始化静态成员变量,这样可以在编译期确定静态成员变量的值,避免了运行时的初始化开销。例如:
        class MyClass {
        public:
            static constexpr int value = 10;
        };

      • 模板参数:可以使用常量表达式作为模板参数,这样可以在编译期根据不同的参数值生成不同的代码。例如:
        template <int N>
        class MyTemplate {
        public:
            // 模板类的实现
        };
        
        int main() {
            MyTemplate<10> obj;
            return 0;
        }

      • 如何在 C++ 中进行更复杂的编译期计算?

        • C++ 中可以使用模板元编程技术进行更复杂的编译期计算。例如,可以使用模板递归、模板特化等技术来实现编译期的数学计算、类型推导等功能。
        • 例如,计算斐波那契数列的编译期版本:
          template <int N>
          struct Fibonacci {
              enum { value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value };
          };
          
          template <>
          struct Fibonacci<0> {
              enum { value = 0 };
          };
          
          template <>
          struct Fibonacci<1> {
              enum { value = 1 };
          };

          在这个例子中,使用模板递归实现了编译期计算斐波那契数列。

 喜欢的同学可以点点关注!这个系列将会持续更新!

 

 

标签:std,八股文,int,C++,程序员,参数,shared,ptr,模板
From: https://blog.csdn.net/a915227127/article/details/142636650

相关文章

  • c++:引用
    一、引用概念是什么?引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"类型&引用变量名(对象名)=引用实体;voidTestRef(){inta=10;int......
  • C++发邮件:如何轻松实现邮件自动化发送?
    C++发邮件的详细步骤与教程指南?如何在C++中发邮件?无论是定期发送报告、通知客户还是管理内部沟通,自动化邮件系统都能显著提升工作效率。AokSend将详细介绍如何使用C++发邮件,实现邮件自动化发送,帮助您轻松管理邮件通信。C++发邮件:配置环境在选择了合适的C++发邮件库之后,接下......
  • 南沙C++信奥赛陈老师解一本通题 1269:【例9.13】庆功会
    ​ 【题目描述】为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。【输入】第一行二个数n(n≤500),m(m≤6000),其中n代表希望购买的奖品的种数,m表示拨款金额。接......
  • 行业机遇!程序员:如何选择适合自己的就业方向?
    随着科技的不断进步和发展,程序员的就业前景也越来越广阔。而在这个快速发展的行业中,在各个领域都有着广泛的应用,信息技术的迅猛发展使得程序员在现代社会中占据了举足轻重的地位。从软件开发到网络安全,再到人工智能,程序员的就业机会广泛且前景光明,为程序员提供了丰富多彩的职......
  • C++ struct和class的异同、C中和C++中struct的异同
    一、前言C++中的struct结构体和C语言中的struct结构体差异较大。C++中的struct结构体和C++中的class类极为相似。二、C++的struct和class1.相同点      (1)成员     struct和class都可以在主体中定义成员变量和成员函数!两者在定义成员变量和成员函数上......
  • C++ string的基本运用详细解剖
    string的基本操作一.与C语言中字符串的区别二.标准库中的string三.string中常用接口的介绍1.string中常用的构造函数2.string类对象的容量操作函数3.string类对象的访问及遍历操作4.string类对象的修改操作5.string类的非成员函数6.string中的其他一些操作一.与C语言......
  • AVLTree【c++实现】
    目录AVL树1.AVL树的概念2.AVLTree节点的定义3.AVLTree的插入4.AVLTree的旋转4.1左单旋4.2右单旋4.3左右双旋4.4右左双旋5.AVLTree的验证6.AVLTree的性能AVL树AVLTree的代码实现连接:AVLTree代码链接1.AVL树的概念学习了二叉搜索树之后,我们知道二叉搜索树虽可以......
  • C++ list容器的使用介绍
     前言:list的底层结构是一个带头双向循环链表,string和vector平时我们用到的比较多,相对于string容器和vector容器,我们对于list相对来说稍微陌生一些,通过文章的介绍和和以后使用到此容器后有遗忘的点,通过官方文档的查阅,加强对于list容器的认识。内容摘要:本文包含四种构造......
  • C++:数据类型
    1C++  1.1什么是C++语言呢?    C++语言是面向过程编程的高级语言,同时也可以向高级语言一样面向对象编程,泛型编程;    C++语言派生自C语言,所以C++语言可以向下兼容C语言;C++相比C,扩展了面向对象编程,泛型编程;    C++语言大小写敏感,支持ANSI标准;    G++编译器......
  • C++ 函数
    定义函数实例函数声明调用函数函数参数参数的默认值Lambda函数与表达式函数是一组一起执行一个任务的语句。每个C++程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。您可以把代码划分到不同的函数中。如何划分代码到不同的函数......