秋招C++八股--函数(持续更新)

1 C++中将临时变量作为返回值时的处理过程

  1. 函数返回值的存储位置:
    • 在C语言中,函数返回值通常会被存储在寄存器中(如ax、eax等),而不是堆栈中。这样设计的目的是为了提高执行效率,避免频繁的堆栈操作。
    • 在函数调用结束后,返回值仍然保留在寄存器中,并且在函数退出时不会被销毁。这意味着返回值可以被调用函数使用,即使临时变量已经被销毁。
  2. 临时变量的生命周期:
    • 临时变量(局部变量)在函数调用过程中通常会被分配到栈内存中。当函数退出时,栈帧会被弹出,临时变量的内存空间被释放,但其值可能仍然存在于寄存器中。
    • 临时变量的内存空间在函数退出后可以被其他变量使用,但使用时需要注意,因为该内存空间的内容可能已经被修改,不再代表原来临时变量的值。
  3. 函数返回值的使用:
    • 函数返回值可以通过赋值语句将其存储到其他变量中,以便在后续代码中使用。
    • 如果不需要显式地将返回值存储到变量中,可以直接在函数调用表达式中使用返回值。

2 C++函数调用的压栈过程

#include <iostream>
using namespace std;
int f(int n)
{
	cout << n << endl;
	return n;
}
void func(int param1, int param2)
{
	int var1 = param1;
	int var2 = param2;
	printf("var1=%d,var2=%d", f(var1), f(var2));//如果将printf换为cout进行输出,输出结果则刚好相反
}
int main(int argc, char* argv[])
{
	func(1, 2);
	return 0;
}
//输出结果
//2
//1
//var1=1,var2=2

当函数从入口函数main函数开始执行时,编译器会将我们操作系统的运行状态,main函数的返回地 址、main的参数、mian函数中的变量、进行依次压栈

当main函数开始调用func()函数时,编译器此时会将main函数的运行状态进行压栈,再将func()函数的返回地址、func()函数的参数从右到左、func()定义变量依次压栈;

当func()调用f()的时候,编译器此时会将func()函数的运行状态进行压栈,再将的返回地址、f()函数的参 数从右到左、f()定义变量依次压栈

从代码的输出结果可以看出,函数f(var1)、f(var2)依次入栈,而后先执行f(var2),再执行f(var1),最后 打印整个字符串,将栈中的变量依次弹出,最后主函数返回。

3 编程实现strcpy函数

char* strcpy_custom(char* strDest, const char* strSrc) {
    char* dest = strDest;
    while (*strSrc) {
        *strDest = *strSrc;
        strDest++;
        strSrc++;
    }
    *strDest = '\0';  // 添加字符串结尾的空字符
    return dest;
}

int main() {


    char dest[20];
    char src[] = "Hello";
    // 
    strcpy_custom(dest, src);

    return 0;
}

通过返回目标字符串的指针,可以方便地在一行代码中连续使用多个字符串操作函数,提高代码的简洁性和可读性。

4 重载、重写、隐藏的区别

  1. 重载(Overload):

    • 定义:在同一个作用域内,允许有多个同名函数或操作符,但它们的参数列表不同(参数类型、个数或顺序不同)。

    • 特点:重载函数具有相同的名称但不同的参数列表,可以根据参数的不同选择正确的函数进行调用。

    • 实现:重载通过函数的参数列表来区分函数,编译器在编译时根据调用的参数类型或个数来确定调用哪个函数。

    #include <iostream>
    
    class Calculator {
    public:
        int add(int a, int b) {
            return a + b;
        }
    
        double add(double a, double b) {
            return a + b;
        }
    };
    
    int main() {
        Calculator calc;
        
        int sum1 = calc.add(3, 4);
        std::cout << "Sum1: " << sum1 << std::endl;
    
        double sum2 = calc.add(2.5, 3.7);
        std::cout << "Sum2: " << sum2 << std::endl;
    
        return 0;
    }
    

2.重写(Override):

  • 定义:在派生类中重新定义基类中的虚函数,使用相同的函数名、参数列表和返回类型。
  • 特点:重写函数实现了基类虚函数的多态性,允许派生类对基类的虚函数进行重新定义。
  • 实现:通过在派生类中使用相同的函数签名来覆盖基类中的虚函数,使得在运行时根据对象的实际类型调用正确的函数实现。
#include <iostream>

class Animal {
public:
    virtual void makeSound() {
        std::cout << "Animal makes a sound" << std::endl;
    }
};

class Cat : public Animal {
public:
    void makeSound() override {
        std::cout << "Cat meows" << std::endl;
    }
};

int main() {
    Animal* animal = new Cat();
    animal->makeSound();  // 输出:Cat meows

    delete animal;
    return 0;
}

3.隐藏(Hide):

  • 定义:在派生类中定义了与基类中的非虚函数同名的函数,隐藏了基类中的同名函数
  • 特点:派生类的同名函数隐藏了基类的函数,使得基类中的函数对于派生类对象不可见。
  • 实现:通过在派生类中定义与基类中的函数具有相同的函数名,使得在使用派生类对象时,只能访问到派生类中定义的同名函数,而无法访问基类中的同名函数。
#include <iostream>

class Base {
public:
    void display() {
        std::cout << "Base display" << std::endl;
    }
};

class Derived : public Base {
public:
    void display() {
        std::cout << "Derived display" << std::endl;
    }
};

int main() {
    Derived derived;
    derived.display();  // 输出:Derived display

    derived.Base::display();  // 输出:Base display

    return 0;
}

5 define、const、typedef、inline的使用方法?他们之间有什么区别?

总结一下:

  1. const与#define的区别:

const定义的常量是带有类型的变量,而#define定义的只是简单的常数。

#define在预处理阶段进行文本替换,而const在编译和链接阶段起作用。

const有数据类型,进行类型检查,可以避免一些低级错误;而#define只是简单的字符串替换,没有类型检查。

#define预处理后占用代码段空间,const占用数据段空间。

const不能重定义,而可以通过#undef取消#define定义的符号,进行重定义。

#define还有独特功能,比如用来防止文件重复引用。

#include <iostream>

const int MAX_VALUE = 100; // 使用const定义常量

#define MAX_VALUE_DEFINE 100 // 使用#define定义常量

int main() {
    const int localValue = 50; // 使用const定义局部变量

    std::cout << "MAX_VALUE: " << MAX_VALUE << std::endl;
    std::cout << "MAX_VALUE_DEFINE: " << MAX_VALUE_DEFINE << std::endl;
    std::cout << "localValue: " << localValue << std::endl;

    // 修改常量的值
    // MAX_VALUE = 200; // 编译错误,const定义的常量不可修改

    // 修改宏定义的值
    #undef MAX_VALUE_DEFINE
    #define MAX_VALUE_DEFINE 200

    std::cout << "Modified MAX_VALUE_DEFINE: " << MAX_VALUE_DEFINE << std::endl;

    return 0;
}

2.#define和typedef的区别:

执行时间不同:typedef在编译阶段起作用,具有类型检查的功能;#define是在预处理阶段进行宏替换,不进行类型检查。

功能差异:typedef用于定义类型的别名、定义与平台无关的数据类型、与struct的结合使用等;而#define不仅可以为类型取别名,还可以定义常量、变量、编译开关等。

作用域不同:#define没有作用域限制,预定义的宏可以在整个程序中使用;而typedef有自己的作用域。

#include <iostream>

#define PI 3.14159 // 使用#define定义常量
typedef double Real; // 使用typedef定义类型别名

int main() {
    Real radius = 2.5; // 使用typedef定义的类型别名
    double circumference = 2 * PI * radius; // 使用#define定义的常量

    std::cout << "Radius: " << radius << std::endl;
    std::cout << "Circumference: " << circumference << std::endl;

    // 修改常量的值
    #undef PI
    #define PI 3.14

    double area = PI * radius * radius;

    std::cout << "Modified PI: " << PI << std::endl;
    std::cout << "Area: " << area << std::endl;

    return 0;
}

3.#define与inline的区别:

#define是关键字,而inline是函数关键字。

宏定义在预处理阶段进行文本替换,而inline函数在编译阶段进行函数替换。

inline函数具有类型检查,相比宏定义更安全。

#include <iostream>

#define SQUARE(x) (x * x) // 使用#define定义宏函数
inline int square(int x) { return x * x; } // 使用inline定义函数

int main() {
    int num = 5;

    int result1 = SQUARE(num + 1); // 使用#define定义的宏函数进行计算
    int result2 = square(num + 1); // 使用inline定义的函数进行计算

    std::cout << "Result 1: " << result1 << std::endl;
    std::cout << "Result 2: " << result2 << std::endl;

    return 0;
}

什么时候使用 inline 函数?

使用inline函数的主要情况是在短小且频繁调用的函数上,以提高程序的执行效率。以下是一些使用inline函数的常见情况:

小型访问函数:如果函数体非常简单,并且频繁被调用,可以将其声明为inline函数。这样可以避免函数调用的开销,直接将函数的代码插入到调用处,提高程序的执行效率。

inline int square(int x) { return x * x; }

类中的成员函数:在类定义中声明的函数,默认情况下是隐式地被视为内联函数。类的成员函数通常被频繁调用,并且函数体一般较短,适合使用inline进行声明。

class MyClass {
public:
    inline void doSomething() {
        // Function body
    }
};

模板函数:在模板函数中,函数的具体实现通常包含在头文件中。为了避免多次包含头文件导致的重复定义错误,可以将模板函数声明为inline。

template <typename T>
inline T max(T a, T b) {
    return (a > b) ? a : b;
}

需要注意的是,使用inline关键字只是对编译器的建议,编译器最终决定是否将函数作为内联函数进行处理。因此,将函数声明为inline并不一定意味着它一定会被内联展开,具体情况还需要根据编译器的优化策略和函数的复杂性来确定。

C++ 校招面试精解 文章被收录于专栏

适用于 1.C++基础薄弱者 2.想快速上手C++者 3.秋招C++方向查漏补缺者 4.秋招C++短期冲刺者

全部评论

相关推荐

投递美团等公司10个岗位
点赞 评论 收藏
转发
4 13 评论
分享
牛客网
牛客企业服务