MST

星途 面试题库

面试题:C++ 引用和指针在内存管理与多态方面的表现差异

当涉及到动态内存分配与释放,以及实现多态时,C++ 的引用和指针在使用方式和产生的效果上有哪些不同?结合代码示例,分析在继承体系下,如何正确使用引用和指针来实现多态,并处理好内存管理问题。
20.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. C++ 引用和指针在动态内存分配与释放、多态方面的不同

  • 使用方式
    • 指针:指针是一个变量,存储的是另一个变量的地址。使用 * 运算符来声明指针变量,通过 & 运算符获取变量地址赋值给指针,使用 * 运算符来访问指针所指向的对象。例如:
int num = 10;
int *ptr = #
std::cout << *ptr << std::endl;
- **引用**:引用是一个已存在变量的别名,一旦初始化后就不能再引用其他变量。使用 `&` 运算符来声明引用变量,并在声明时必须初始化。例如:
int num = 10;
int &ref = num;
std::cout << ref << std::endl;
  • 动态内存分配与释放
    • 指针:在动态内存分配时,使用 new 运算符返回一个指向新分配内存的指针,使用 delete 运算符来释放该内存。例如:
int *dynamicPtr = new int(20);
delete dynamicPtr;
- **引用**:引用本身不能直接用于动态内存分配,但可以作为函数参数或成员变量间接参与动态内存管理。由于引用在初始化后不能改变,因此它不适合直接控制动态内存的释放。例如:
class MyClass {
public:
    MyClass(int value) : data(value) {}
    ~MyClass() { std::cout << "Deleting MyClass" << std::endl; }
private:
    int data;
};

void process(MyClass &obj) {
    // 处理 obj
}

int main() {
    MyClass *objPtr = new MyClass(10);
    MyClass &objRef = *objPtr;
    process(objRef);
    delete objPtr;
    return 0;
}
  • 多态效果
    • 指针:通过基类指针指向派生类对象,可以实现多态。在调用虚函数时,根据指针实际指向的对象类型来决定调用哪个函数版本。例如:
class Shape {
public:
    virtual void draw() { std::cout << "Drawing a shape" << std::endl; }
};

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

int main() {
    Shape *shapePtr = new Circle();
    shapePtr->draw(); // 调用 Circle 的 draw 函数
    delete shapePtr;
    return 0;
}
- **引用**:通过基类引用绑定派生类对象,同样可以实现多态。与指针类似,在调用虚函数时,根据引用所绑定的实际对象类型来决定调用哪个函数版本。例如:
class Shape {
public:
    virtual void draw() { std::cout << "Drawing a shape" << std::endl; }
};

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

int main() {
    Circle circle;
    Shape &shapeRef = circle;
    shapeRef.draw(); // 调用 Circle 的 draw 函数
    return 0;
}

2. 在继承体系下正确使用引用和指针实现多态并处理内存管理

  • 使用指针实现多态与内存管理
class Animal {
public:
    virtual void speak() { std::cout << "Animal speaks" << std::endl; }
    virtual ~Animal() { std::cout << "Destroying animal" << std::endl; }
};

class Dog : public Animal {
public:
    void speak() override { std::cout << "Dog barks" << std::endl; }
    ~Dog() { std::cout << "Destroying dog" << std::endl; }
};

class Cat : public Animal {
public:
    void speak() override { std::cout << "Cat meows" << std::endl; }
    ~Cat() { std::cout << "Destroying cat" << std::endl; }
};

int main() {
    Animal *animalPtr1 = new Dog();
    Animal *animalPtr2 = new Cat();

    animalPtr1->speak();
    animalPtr2->speak();

    delete animalPtr1;
    delete animalPtr2;
    return 0;
}

在这个例子中,通过 Animal 指针指向 DogCat 对象实现多态。注意要在使用完后通过 delete 释放动态分配的内存,并且基类的析构函数要声明为虚函数,以确保在删除基类指针时能正确调用派生类的析构函数。

  • 使用引用实现多态与内存管理
class Animal {
public:
    virtual void speak() { std::cout << "Animal speaks" << std::endl; }
    virtual ~Animal() { std::cout << "Destroying animal" << std::endl; }
};

class Dog : public Animal {
public:
    void speak() override { std::cout << "Dog barks" << std::endl; }
    ~Dog() { std::cout << "Destroying dog" << std::endl; }
};

class Cat : public Animal {
public:
    void speak() override { std::cout << "Cat meows" << std::endl; }
    ~Cat() { std::cout << "Destroying cat" << std::endl; }
};

void makeSound(Animal &animal) {
    animal.speak();
}

int main() {
    Dog dog;
    Cat cat;

    makeSound(dog);
    makeSound(cat);
    return 0;
}

这里通过 Animal 引用绑定 DogCat 对象实现多态。由于引用本身不负责内存分配与释放,所以不需要考虑 delete 操作。但在这种情况下,对象的生命周期通常由外部管理,比如在栈上创建对象,函数结束时对象自动销毁。