面试题答案
一键面试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
指针指向 Dog
和 Cat
对象实现多态。注意要在使用完后通过 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
引用绑定 Dog
和 Cat
对象实现多态。由于引用本身不负责内存分配与释放,所以不需要考虑 delete
操作。但在这种情况下,对象的生命周期通常由外部管理,比如在栈上创建对象,函数结束时对象自动销毁。