每日计划-1104

1. 完成 704 二分查找

#include <vector>

class Solution {
public:
    
    int search(vector<int>& nums, int target) {
        // 获取输入向量nums的大小,并存储在变量n中
        int n = nums.size();

        // 开始一个循环,从索引0遍历到向量nums的最后一个元素(索引为n - 1)
        for (int i = 0; i < n; i++) {
            // 在每次循环中,检查当前索引i对应的向量元素nums[i]是否等于目标值target
            if (nums[i] == target)
                // 如果找到了目标值,就返回当前元素的索引i,表示目标值在向量中的位置
                return i;
        }

        // 如果循环结束后都没有找到目标值target,那么就返回 -1,表示目标值不在给定的向量nums中
        return -1;
    }
};

2. 八股部分

1) C++ 中类的三大特性是什么?请简要解释。

封装

  • 概念
    • 封装是将类的成员变量(数据)和成员函数(操作这些数据的方法)组合在一起形成一个逻辑单元,即类。并且通过访问控制修饰符(public、private、protected)来控制外部对类内部成员的访问权限。
  • 作用及示例
    • 数据隐藏与保护:通过将成员变量设置为 private 或 protected,隐藏了数据的具体实现细节,防止外部代码直接访问和随意修改这些数据,从而保证数据的安全性和完整性。例如:
class BankAccount {
private:
    double balance; // 账户余额,设置为私有,外部无法直接访问

public:
    // 存款函数,通过该函数来修改余额
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    // 查询余额函数,允许外部获取余额信息
    double getBalance() {
        return balance;
    }
};

在上述例子中,balance 作为私有成员变量被隐藏起来,外部只能通过 deposit 和 getBalance 这两个公共成员函数来间接操作和获取余额信息。

  • 实现模块化编程:类作为一个独立的模块,其内部实现对外部是封装的,外部只需要关注类提供的公共接口(公共成员函数)就可以使用该类。这使得代码更易于维护、扩展和复用,不同的模块可以独立开发和测试,只要保证接口的一致性即可。

继承

  • 概念
    • 继承允许一个类(子类或派生类)从另一个类(父类或基类)获取其已有的属性(成员变量)和方法(成员函数)。子类可以在继承的基础上添加新的成员变量、成员函数,或者对父类的某些成员函数进行重写(覆盖)以满足自身特定的需求。
  • 作用及示例
    • 代码复用:子类无需重新定义父类中已有的功能,直接继承过来即可使用,大大减少了代码的编写工作量。例如,考虑一个动物类层次结构:
class Animal {
public:
    void eat() {
        cout << "Animal is eating." << endl;
    }

    void sleep() {
        cout << "Animal is sleeping." << endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        cout << "Dog is barking." << endl;
    }
};

在这个例子中,Dog 类继承自 Animal 类,Dog 类继承了 Animal 类的 eat 和 sleep 功能,无需重新实现,只需要添加自己特有的 bark 功能即可。

  • 构建层次化的类结构:可以通过继承创建具有层次关系的类体系,便于对不同类型的对象进行统一管理和分类处理。比如在图形相关的编程中,可以有一个基类 Shape,然后矩形类 Rectangle、圆形类 Circle 等都可以继承自 Shape 类,这样可以针对不同形状的图形进行统一的操作,如计算面积、周长等。

多态

  • 概念
    • 多态是指同一个函数名在不同的类中有不同的实现方式,并且在运行时(动态多态)或编译时(静态多态)根据对象的实际类型来决定调用哪个类中的函数实现。
  • 分类及示例
静态多态
  • 函数重载:在同一作用域内,函数名相同但参数列表不同(参数个数、参数类型或参数顺序不同)的多个函数构成函数重载。在编译时,编译器会根据调用函数时传入的实际参数来确定调用哪个具体的重载函数。例如:
class MathUtils {
public:
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
};

在上述例子中,add 函数有两个重载形式,一个用于整数相加,一个用于小数相加。当调用 add 函数时,编译器根据传入的参数类型就能确定调用哪一个重载函数。

  • 运算符重载:允许用户重新定义运算符的操作含义,使其能适用于自定义的类类型。例如,对于复数类,可以重载 + 运算符来实现复数的加法运算:
class Complex {
private:
    double real;
    double imag;

public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}

    Complex operator+(const Complex& other) {
        Complex result;
        result.real = real + other.real;
        result.imag = imag + other.imag;
        return result;
    }
};

在这个例子中,通过重载 + 运算符,使得 Complex 类的对象可以像普通数字一样进行加法运算。

  • 模板:模板是一种泛型编程技术,它允许编写代码时不指定具体的数据类型,而是在编译时根据实际使用的类型来生成具体的代码。例如,创建一个模板函数来实现两个数的交换:

template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

这里的 swap 函数可以用于交换任意类型的两个变量,只要在调用时指定具体的类型即可。在编译时,编译器会根据传入的实际类型生成相应的具体代码。

动态多态
  • 实现机制:通过虚函数和父类指针或引用子类对象来实现。父类中定义虚函数,子类重写(覆盖)该虚函数,当父类指针或引用指向子类对象时,通过该指针或引用调用重写后的函数,在运行时根据对象的实际类型来确定调用哪个类中的函数实现。例如:

class Shape {
public:
    virtual void draw() {
        cout << "Drawing a generic shape." << endl;
    }
};

class Circle : public Shape {
public:
    void draw() {
        cout << "Drawing a circle." << endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() {
        cout << "Drawing a rectangle." << endl;
}
};

int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Rectangle();

    shape1->draw();
    shape2->draw();

    delete shape1;
    delete shape2;

    return 0;
}

  在上述例子中,Shape 类中定义了虚函数 drawCircle 类和 Rectangle 类分别重写了该虚函数。在 main 函数中,通过父类指针 shape1 和 shape2 分别指向 Circle 类和 Rectangle 类的对象,当调用 draw 函数时,在运行时根据指针所指向的实际对象类型(Circle 或 Rectangle)来确定调用哪个类中的 draw 函数实现。

  多态性使得代码更加灵活和可扩展,无论是静态多态还是动态多态,都在不同方面提高了 C++ 代码的复用性和适应性。

2)什么是构造函数和析构函数?它们的作用是什么?

  • 创建对象的时候编译器自动调用构造函数

  • 构造函数和类名相同,没有返回值也不写 void

  • 如果没有实现构造函数编译器会提供默认的无参构造函数

  • 构造函数是给成员变量赋值的不是初始化(引出初始化参数列表)

移动构造 继承构造 委托构造

移动构造函数

“移动构造函数存在的目的在于实现资源的移动,而不是复制,以提高性能。它接受一个右值引用,允许我们转移资源而非拷贝。”

class Example {  
public:    std::vector<intdata;  
    Example(std::vector<intvec) : data(std::move(vec)) { }  // 移动构造函数  
};  

继承构造函数

“当我们创建一个派生类时,可以在派生类的构造函数中调用基类的构造函数,以确保基类部分的正确初始化。这可以通过初始化参数列表实现。”

class Base {  
public:  Base(int x) { }  
};  

class Derived : public Base {  
public:  Derived(int x) : Base(x) { }  // 继承构造  
};  

委托构造函数

“委托构造函数允许一个构造函数调用另一个构造函数。这有助于减少代码重复,提高可维护性。例如,一个构造函数可以委托另一个构造函数处理实际的初始化逻辑。”

class Example {  
public:  int a;  
    Example() : Example(42) { }  // 委托构造  Example(int val) : a(val) { }  
};  

析构函数

对象被释放的时候编译器自动调用析构函数

析构函数的名为~+类名,没有返回值也不写 void

如果没有实现析构函数编译器会提供默认的析构函数

析构函数是释放对象里面成员变量指向的堆区内存的 如下代码

class A
{
        int* p = nullptr;
public:
        A(int n) {
                if (n > 0)p = new int[n];
        }
        ~A() {

                if (p)delete[]p;
        }
};

构造函数的调用顺序:

  • 当一个对象被创建时,首先调用基类的构造函数,然后是派生类的构造函数。如果构造函数是虚函数,编译器就会在构造期间尝试查找虚函数表,但此时虚函数表仍未初始化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值