Cpp Cpp C++高频面试题精选 - 第一部分(题目1-10) NyxX 2026-02-01 2026-02-01 C++高频面试题精选 - 第一部分(题目1-10) 基础知识 题目1: 简述C++中new和malloc的区别 解答:
类型 :new是C++操作符,malloc是C库函数
返回类型 :new返回具体类型指针,malloc返回void*需要强制转换
构造/析构 :new会调用构造函数,delete调用析构函数;malloc不会
内存分配失败 :new抛出std::bad_alloc异常,malloc返回NULL
大小计算 :new自动计算大小,malloc需要手动指定字节数
重载 :new可以重载,malloc不可以
内存来源 :new从自由存储区分配,malloc从堆分配(通常相同但概念不同)
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int * p1 = new int (10 ); int * arr1 = new int [5 ]; delete p1;delete [] arr1;int * p2 = (int *)malloc (sizeof (int )); int * arr2 = (int *)malloc (5 * sizeof (int )); if (p2 == NULL ) { } free (p2);free (arr2);class Widget {public : Widget () { cout << "构造函数\n" ; } ~Widget () { cout << "析构函数\n" ; } }; Widget* w1 = new Widget; delete w1; Widget* w2 = (Widget*)malloc (sizeof (Widget)); free (w2);
口头解答: “这是个很经典的问题。首先,new是C++的操作符,而malloc是C语言的库函数。最关键的区别是new会自动调用对象的构造函数,delete会调用析构函数,这对于C++的对象管理非常重要;而malloc只是分配一块原始内存,不会做任何初始化。另外,new返回的是具体类型的指针,类型安全;malloc返回void指针,需要强制转换。在错误处理上,new失败会抛出异常,malloc失败返回NULL。所以在C++中,我们应该优先使用new而不是malloc。”
题目2: C++中的引用和指针有什么区别? 解答:
本质 :引用是别名,指针是地址变量
初始化 :引用必须在声明时初始化,指针可以不初始化
可变性 :引用一旦绑定不可改变,指针可以指向不同对象
空值 :引用不能为空,指针可以为nullptr
运算 :指针支持自增、自减等运算,引用不支持
内存占用 :引用通常不占额外空间(编译器优化),指针占用空间存储地址
多级 :可以有多级指针,但引用只有一级
使用 :引用使用更安全简洁,指针更灵活强大
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int x = 10 ;int & ref = x; ref = 20 ; int * ptr; ptr = &x; *ptr = 30 ; ptr = nullptr ; int y = 40 ;ptr = &y; int ** pp = &ptr; void func1 (int & r) { r = 100 ; } void func2 (int * p) { if (p != nullptr ) { *p = 100 ; } } func1 (x); func2 (&x);
口头解答: “引用和指针都可以实现间接访问,但它们有本质区别。引用可以理解为变量的别名,一旦绑定就不能改变,而且必须在声明时初始化,不能为空。指针则是一个存储地址的变量,可以随时改变指向,也可以为空。在使用上,引用更安全也更简洁,因为不需要解引用操作,编译器会帮我们处理;指针则更灵活,可以进行算术运算,可以实现多级间接访问。一般来说,能用引用的地方优先用引用,需要重新绑定或可能为空的情况才用指针。”
题目3: 什么是左值和右值?C++11中的右值引用有什么作用? 解答:
左值(lvalue) :有持久地址的表达式,可以取地址,可以出现在赋值号左边
右值(rvalue) :临时对象或字面量,不能取地址,只能出现在赋值号右边
右值引用(&&) :C++11引入,用于绑定到右值
作用 :
实现移动语义,避免不必要的拷贝
完美转发(perfect forwarding)
提高性能,特别是对于临时对象
移动构造/赋值 :通过”窃取”资源而非拷贝来提高效率
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 int x = 10 ; int * p = &x; int && rref = 10 ; int && rref2 = x + 5 ; class String { char * data; size_t len; public : String (const String& s) { len = s.len; data = new char [len + 1 ]; strcpy (data, s.data); cout << "拷贝构造\n" ; } String (String&& s) noexcept { data = s.data; len = s.len; s.data = nullptr ; s.len = 0 ; cout << "移动构造\n" ; } ~String () { delete [] data; } }; String createString () { String s ("hello" ) ; return s; } String s1 ("world" ) ;String s2 = s1; String s3 = createString (); String s4 = std::move (s1); vector<int > v1 (1000000 , 1 ) ;vector<int > v2 = v1; vector<int > v3 = std::move (v1);
口头解答: “简单来说,左值就是有名字、有持久地址的对象,比如变量;右值就是临时的、即将销毁的对象,比如函数返回的临时对象或字面量。C++11引入的右值引用是个重大特性,它允许我们区分对待临时对象。最重要的应用就是移动语义——当我们知道一个对象是临时的、马上要销毁时,与其费力地拷贝它的资源,不如直接’偷’过来用。这在处理大对象,比如vector、string时,性能提升非常明显。我们通过实现移动构造函数和移动赋值运算符来支持这个特性。”
题目4: 解释const关键字的各种用法 解答:
1. const变量:
2. const指针:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int x = 10 , y = 20 ;const int * p1 = &x;p1 = &y; int * const p2 = &x;*p2 = 30 ; const int * const p3 = &x;
3. const成员函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Rectangle { int width, height; public : Rectangle (int w, int h) : width (w), height (h) {} int getArea () const { return width * height; } void setWidth (int w) { width = w; } void display () const { cout << width << "x" << height; } }; const Rectangle rect (10 , 20 ) ;cout << rect.getArea ();
4. const引用:
1 2 3 4 5 6 7 8 9 10 11 void process (const string& str) { cout << str; } string s = "hello" ; process (s); const int & ref = 10 ;
5. const返回值:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Data { string name; public : const string& getName () const { return name; } const Data* getPtr () const { return this ; } };
6. mutable关键字:
1 2 3 4 5 6 7 8 9 class Counter { mutable int accessCount; int value; public : int getValue () const { ++accessCount; return value; } };
口头解答: “const在C++中非常常用,主要表示’不可修改’的语义。最基础的是const变量,声明后不能改变。在指针中,const的位置很关键:const在星号左边表示指向的内容不可变,在星号右边表示指针本身不可变。在类中,const成员函数承诺不修改对象的成员变量,这样const对象也能调用这些函数。在函数参数中,const引用是个很好的实践,既避免了拷贝开销,又保证了参数不被修改。总的来说,合理使用const可以提高代码的安全性和可读性,是现代C++的重要实践。”
题目5: static关键字在C++中有哪些用法? 解答:
1. 静态局部变量:
1 2 3 4 5 6 7 8 9 10 void counter () { static int count = 0 ; count++; cout << count << endl; } counter (); counter (); counter ();
2. 静态全局变量/函数(文件作用域):
1 2 3 4 5 6 7 8 9 static int internal_var = 10 ; static void helper () { }
3. 静态成员变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Counter { static int totalCount; int instanceCount; public : Counter () { totalCount++; instanceCount = totalCount; } static int getTotalCount () { return totalCount; } }; int Counter::totalCount = 0 ;Counter c1, c2, c3; cout << Counter::getTotalCount (); cout << c1. getTotalCount ();
4. 静态成员函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Math {public : static int add (int a, int b) { return a + b; } static double pi () { return 3.14159 ; } }; int result = Math::add (10 , 20 );double p = Math::pi ();
5. 类内静态常量:
1 2 3 4 5 6 7 8 9 10 class Config {public : static const int MAX_SIZE = 100 ; static const string DEFAULT_NAME; static constexpr double PI = 3.14159 ; }; const string Config::DEFAULT_NAME = "default" ;
6. 单例模式应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Singleton { static Singleton* instance; Singleton () {} public : static Singleton* getInstance () { if (instance == nullptr ) { instance = new Singleton (); } return instance; } }; Singleton* Singleton::instance = nullptr ;
完整示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class BankAccount { static double interestRate; static int accountCount; string accountNumber; double balance; public : BankAccount (double initial) : balance (initial) { accountCount++; accountNumber = "ACC" + to_string (accountCount); } static void setInterestRate (double rate) { interestRate = rate; } static int getAccountCount () { return accountCount; } double calculateInterest () const { return balance * interestRate; } }; double BankAccount::interestRate = 0.05 ;int BankAccount::accountCount = 0 ;BankAccount::setInterestRate (0.06 ); BankAccount acc1 (1000 ) , acc2 (2000 ) ;cout << BankAccount::getAccountCount ();
口头解答: “static在C++中有多种用途。在函数内部,static变量只会初始化一次,之后调用函数时会保持上次的值,这常用于计数或缓存。在类中,static成员属于整个类而不是某个对象,所有对象共享同一份数据。静态成员函数不需要对象就能调用,但也因此只能访问静态成员。另外,在全局作用域用static可以限制变量或函数的链接性,让它只在当前文件可见,这在大项目中有助于避免命名冲突。总的来说,static关键字用于控制变量的生命周期、作用域和所属关系。”
面向对象编程 题目6: 解释C++中的三大特性:封装、继承、多态 解答:
1. 封装(Encapsulation):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class BankAccount {private : string accountNumber; double balance; public : BankAccount (string accNum, double initial) : accountNumber (accNum), balance (initial) {} void deposit (double amount) { if (amount > 0 ) { balance += amount; } } bool withdraw (double amount) { if (amount > 0 && balance >= amount) { balance -= amount; return true ; } return false ; } double getBalance () const { return balance; } }; BankAccount account ("123456" , 1000 ) ;account.deposit (500 ); cout << account.getBalance ();
2. 继承(Inheritance):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Animal {protected : string name; int age; public : Animal (string n, int a) : name (n), age (a) {} void eat () { cout << name << " is eating\n" ; } virtual void makeSound () { cout << "Some sound\n" ; } }; class Dog : public Animal { string breed; public : Dog (string n, int a, string b) : Animal (n, a), breed (b) {} void makeSound () override { cout << "Woof!\n" ; } void wagTail () { cout << name << " is wagging tail\n" ; } }; class Cat : public Animal {public : Cat (string n, int a) : Animal (n, a) {} void makeSound () override { cout << "Meow!\n" ; } }; Dog dog ("Buddy" , 3 , "Golden Retriever" ) ;dog.eat (); dog.makeSound (); dog.wagTail ();
3. 多态(Polymorphism):
编译时多态(静态多态):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Calculator {public : int add (int a, int b) { return a + b; } double add (double a, double b) { return a + b; } int add (int a, int b, int c) { return a + b + c; } }; class Complex { double real, imag; public : Complex (double r, double i) : real (r), imag (i) {} Complex operator +(const Complex& other) { return Complex (real + other.real, imag + other.imag); } }; template <typename T>T maximum (T a, T b) { return (a > b) ? a : b; }
运行时多态(动态多态):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class Shape {public : virtual double area () const = 0 ; virtual void draw () const = 0 ; virtual ~Shape () {} }; class Circle : public Shape { double radius; public : Circle (double r) : radius (r) {} double area () const override { return 3.14159 * radius * radius; } void draw () const override { cout << "Drawing circle\n" ; } }; class Rectangle : public Shape { double width, height; public : Rectangle (double w, double h) : width (w), height (h) {} double area () const override { return width * height; } void draw () const override { cout << "Drawing rectangle\n" ; } }; void processShape (const Shape& shape) { cout << "Area: " << shape.area () << endl; shape.draw (); } Circle circle (5 ) ;Rectangle rect (4 , 6 ) ;processShape (circle); processShape (rect); vector<Shape*> shapes; shapes.push_back (new Circle (3 )); shapes.push_back (new Rectangle (4 , 5 )); for (Shape* shape : shapes) { shape->draw (); cout << "Area: " << shape->area () << endl; } for (Shape* shape : shapes) { delete shape; }
三大特性协同工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class GraphicsEditor { vector<unique_ptr<Shape>> shapes; public : void addShape (unique_ptr<Shape> shape) { shapes.push_back (std::move (shape)); } void renderAll () { for (const auto & shape : shapes) { shape->draw (); } } double totalArea () { double total = 0 ; for (const auto & shape : shapes) { total += shape->area (); } return total; } }; GraphicsEditor editor; editor.addShape (make_unique <Circle>(5 )); editor.addShape (make_unique <Rectangle>(4 , 6 )); editor.renderAll ();
口头解答: “面向对象的三大特性是C++的核心。封装就是把数据和方法包装在类里,通过访问控制符来保护数据,只暴露必要的接口,这样可以隐藏实现细节,提高安全性。继承让我们可以基于已有的类创建新类,实现代码复用,建立类之间的层次关系。多态是最强大的特性,它允许用统一的接口调用不同的实现。C++既支持编译时多态,比如函数重载和模板;也支持运行时多态,主要通过虚函数实现。这三个特性结合起来,让我们能够设计出灵活、可扩展、易维护的代码。”
题目7: 什么是虚函数?virtual关键字的作用是什么? 解答:
1. 虚函数的定义和作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Animal {public : virtual void makeSound () { cout << "Some generic sound\n" ; } void breathe () { cout << "Breathing...\n" ; } virtual ~Animal () {} }; class Dog : public Animal {public : void makeSound () override { cout << "Woof!\n" ; } void breathe () { cout << "Dog breathing\n" ; } }; Animal* animal1 = new Animal (); Animal* animal2 = new Dog (); animal1->makeSound (); animal2->makeSound (); animal1->breathe (); animal2->breathe (); delete animal1;delete animal2;
2. 虚函数的实现原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Base {public : virtual void func1 () { cout << "Base::func1\n" ; } virtual void func2 () { cout << "Base::func2\n" ; } void func3 () { cout << "Base::func3\n" ; } }; class Derived : public Base {public : void func1 () override { cout << "Derived::func1\n" ; } }; Base* ptr = new Derived (); ptr->func1 ();
3. 虚析构函数的重要性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class Base {public : Base () { cout << "Base构造\n" ; } ~Base () { cout << "Base析构\n" ; } }; class Derived : public Base { int * data; public : Derived () : data (new int [100 ]) { cout << "Derived构造\n" ; } ~Derived () { delete [] data; cout << "Derived析构\n" ; } }; Base* ptr = new Derived (); delete ptr; class BaseCorrect {public : virtual ~BaseCorrect () { cout << "Base析构\n" ; } }; class DerivedCorrect : public BaseCorrect { int * data; public : DerivedCorrect () : data (new int [100 ]) {} ~DerivedCorrect () override { delete [] data; cout << "Derived析构\n" ; } }; BaseCorrect* ptr2 = new DerivedCorrect (); delete ptr2;
4. override和final关键字(C++11):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Base {public : virtual void func1 () {} virtual void func2 () {} virtual void func3 () final {} }; class Derived : public Base {public : void func1 () override {} }; class FinalClass final { };
5. 纯虚函数和抽象类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class AbstractShape {public : virtual double area () const = 0 ; virtual void draw () const = 0 ; void printInfo () const { cout << "Area: " << area () << endl; } virtual ~AbstractShape () {} }; class ConcreteCircle : public AbstractShape { double radius; public : ConcreteCircle (double r) : radius (r) {} double area () const override { return 3.14159 * radius * radius; } void draw () const override { cout << "Drawing circle\n" ; } }; ConcreteCircle circle (5 ) ; AbstractShape* shape = &circle; shape->draw ();
6. 虚函数的性能开销:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class WithVirtual {public : virtual void func () {} }; class WithoutVirtual {public : void func () {} }; cout << sizeof (WithVirtual); cout << sizeof (WithoutVirtual);
口头解答: “虚函数是实现C++运行时多态的关键机制。当你把基类的函数声明为virtual,派生类可以重写它,这样通过基类指针或引用调用时,会根据对象的实际类型来决定调用哪个版本,而不是根据指针类型。实现原理是通过虚函数表,编译器为每个包含虚函数的类生成一个函数指针表,对象里会有个隐藏的指针指向这个表。这样运行时就能找到正确的函数。特别要注意的是,如果一个类会被继承,它的析构函数一定要是虚函数,否则通过基类指针删除派生类对象时,不会调用派生类的析构函数,导致资源泄漏。虚函数有轻微的性能开销,但换来了极大的灵活性。”
题目8: 纯虚函数和抽象类是什么? 解答:
1. 纯虚函数的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Shape {public : virtual double area () const = 0 ; virtual double perimeter () const = 0 ; virtual void draw () const = 0 ; virtual ~Shape () {} void printArea () const { cout << "Area: " << area () << endl; } };
2. 抽象类的特点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class AbstractBase {public : virtual void pureVirtual () = 0 ; virtual void normalVirtual () { cout << "Normal virtual\n" ; } void nonVirtual () { cout << "Non-virtual\n" ; } int data; }; AbstractBase* ptr; AbstractBase& ref = ...;
3. 实现抽象类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Circle : public Shape { double radius; public : Circle (double r) : radius (r) {} double area () const override { return 3.14159 * radius * radius; } double perimeter () const override { return 2 * 3.14159 * radius; } void draw () const override { cout << "Drawing circle with radius " << radius << endl; } }; class Rectangle : public Shape { double width, height; public : Rectangle (double w, double h) : width (w), height (h) {} double area () const override { return width * height; } double perimeter () const override { return 2 * (width + height); } void draw () const override { cout << "Drawing rectangle " << width << "x" << height << endl; } }; Circle circle (5 ) ;Rectangle rect (4 , 6 ) ;circle.printArea (); rect.draw (); vector<Shape*> shapes; shapes.push_back (&circle); shapes.push_back (&rect); for (Shape* shape : shapes) { shape->draw (); cout << "Perimeter: " << shape->perimeter () << endl; }
4. 部分实现的抽象类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class PartiallyAbstract : public Shape {public : double area () const override { return 0 ; } }; class FullyImplemented : public PartiallyAbstract {public : double perimeter () const override { return 0 ; } void draw () const override { cout << "Drawing\n" ; } }; FullyImplemented obj;
5. 接口类(只有纯虚函数):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class IDrawable {public : virtual void draw () const = 0 ; virtual ~IDrawable () {} }; class IPrintable {public : virtual void print () const = 0 ; virtual ~IPrintable () {} }; class Document : public IDrawable, public IPrintable { string content; public : void draw () const override { cout << "Drawing document\n" ; } void print () const override { cout << "Printing: " << content << endl; } };
6. 纯虚函数也可以有实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Base {public : virtual void func () = 0 ; }; void Base::func () { cout << "Default implementation\n" ; } class Derived : public Base {public : void func () override { Base::func (); cout << "Derived implementation\n" ; } }; Derived d; d.func ();
7. 实际应用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class IDatabase {public : virtual bool connect (const string& connectionString) = 0 ; virtual bool execute (const string& query) = 0 ; virtual void disconnect () = 0 ; virtual ~IDatabase () {} }; class MySQLDatabase : public IDatabase {public : bool connect (const string& connectionString) override { cout << "Connecting to MySQL...\n" ; return true ; } bool execute (const string& query) override { cout << "Executing MySQL query: " << query << endl; return true ; } void disconnect () override { cout << "Disconnecting from MySQL\n" ; } }; class PostgreSQLDatabase : public IDatabase {public : bool connect (const string& connectionString) override { cout << "Connecting to PostgreSQL...\n" ; return true ; } bool execute (const string& query) override { cout << "Executing PostgreSQL query: " << query << endl; return true ; } void disconnect () override { cout << "Disconnecting from PostgreSQL\n" ; } }; void performDatabaseOperation (IDatabase& db) { db.connect ("server=localhost" ); db.execute ("SELECT * FROM users" ); db.disconnect (); } MySQLDatabase mysql; PostgreSQLDatabase postgres; performDatabaseOperation (mysql); performDatabaseOperation (postgres);
口头解答: “纯虚函数就是在声明时赋值为0的虚函数,它在基类中没有实现,强制要求派生类必须提供实现。包含纯虚函数的类就是抽象类,抽象类不能直接创建对象,只能用作基类。这个机制在C++中用来定义接口,规定派生类必须实现哪些功能。比如我们定义一个Shape抽象类,规定所有形状都必须实现计算面积和绘制的功能,具体的Circle、Rectangle类就必须提供这些实现。这样可以确保接口的统一性,也体现了设计模式中的依赖倒置原则。虽然纯虚函数通常没有实现,但C++允许为它提供默认实现,派生类可以选择调用。”
题目9: 构造函数和析构函数的调用顺序是怎样的? 解答:
1. 单个类的构造和析构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Widget { int * data; public : Widget () { cout << "Widget构造\n" ; data = new int [10 ]; } ~Widget () { cout << "Widget析构\n" ; delete [] data; } }; Widget w;
2. 有成员对象的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Member {public : Member () { cout << "Member构造\n" ; } ~Member () { cout << "Member析构\n" ; } }; class Container { Member m1; Member m2; public : Container () { cout << "Container构造\n" ; } ~Container () { cout << "Container析构\n" ; } }; Container c;
3. 继承关系的构造和析构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Base {public : Base () { cout << "Base构造\n" ; } ~Base () { cout << "Base析构\n" ; } }; class Derived : public Base {public : Derived () { cout << "Derived构造\n" ; } ~Derived () { cout << "Derived析构\n" ; } }; Derived d;
4. 复杂的继承+成员对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Base {public : Base () { cout << "Base构造\n" ; } ~Base () { cout << "Base析构\n" ; } }; class Member {public : Member () { cout << "Member构造\n" ; } ~Member () { cout << "Member析构\n" ; } }; class Derived : public Base { Member m; public : Derived () { cout << "Derived构造\n" ; } ~Derived () { cout << "Derived析构\n" ; } }; Derived d;
5. 多重继承:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Base1 {public : Base1 () { cout << "Base1构造\n" ; } ~Base1 () { cout << "Base1析构\n" ; } }; class Base2 {public : Base2 () { cout << "Base2构造\n" ; } ~Base2 () { cout << "Base2析构\n" ; } }; class Derived : public Base1, public Base2 {public : Derived () { cout << "Derived构造\n" ; } ~Derived () { cout << "Derived析构\n" ; } }; Derived d;
6. 虚继承:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Virtual {public : Virtual () { cout << "Virtual构造\n" ; } ~Virtual () { cout << "Virtual析构\n" ; } }; class Base1 : virtual public Virtual {public : Base1 () { cout << "Base1构造\n" ; } ~Base1 () { cout << "Base1析构\n" ; } }; class Base2 : virtual public Virtual {public : Base2 () { cout << "Base2构造\n" ; } ~Base2 () { cout << "Base2析构\n" ; } }; class Derived : public Base1, public Base2 {public : Derived () { cout << "Derived构造\n" ; } ~Derived () { cout << "Derived析构\n" ; } }; Derived d;
7. 初始化列表的顺序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Example { int a; int b; int c; public : Example (int x) : c (x), b (c), a (b) { } Example (int x) : a (x), b (x), c (x) {} };
8. 静态成员的初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class WithStatic { static int staticMember; int normalMember; public : WithStatic () { cout << "WithStatic构造\n" ; } }; int WithStatic::staticMember = [](){ cout << "静态成员初始化\n" ; return 42 ; }();
9. 完整示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Resource {public : Resource () { cout << "Resource构造\n" ; } ~Resource () { cout << "Resource析构\n" ; } }; class Base {protected : Resource baseRes; public : Base () { cout << "Base构造\n" ; } virtual ~Base () { cout << "Base析构\n" ; } }; class Member {public : Member () { cout << "Member构造\n" ; } ~Member () { cout << "Member析构\n" ; } }; class Derived : public Base { Member m1; Member m2; public : Derived () { cout << "Derived构造\n" ; } ~Derived () override { cout << "Derived析构\n" ; } }; void test () { cout << "创建对象...\n" ; Derived d; cout << "对象使用中...\n" ; } test ();
口头解答: “这个顺序很重要,记住一个原则:构造从根到叶,析构从叶到根,严格相反。对于单个对象,先构造成员变量,再执行构造函数体;析构时相反。在继承中,先构造基类,保证基类的部分准备好了,派生类才能在此基础上构造;然后是派生类的成员;最后执行派生类构造函数体。析构时完全倒过来。虚继承是个特例,虚基类由最底层的派生类负责构造,所以虚基类最先构造,最后析构。还要注意,成员的初始化顺序由它们在类中的声明顺序决定,不是初始化列表的顺序。理解这个顺序对于避免资源管理问题很关键。”
题目10: 什么是拷贝构造函数?深拷贝和浅拷贝的区别? 解答:
1. 拷贝构造函数的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class String { char * data; size_t length; public : String (const String& other) { length = other.length; data = new char [length + 1 ]; strcpy (data, other.data); cout << "拷贝构造\n" ; } String (const char * str) { length = strlen (str); data = new char [length + 1 ]; strcpy (data, str); } ~String () { delete [] data; } }; String s1 ("hello" ) ;String s2 = s1; String s3 (s1) ; void func (String s) {} func (s1);String getStr () { String temp ("world" ) ; return temp; } String s4 = getStr ();
2. 浅拷贝的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class ShallowCopy { int * data; public : ShallowCopy (int value) { data = new int (value); } ~ShallowCopy () { delete data; } }; ShallowCopy obj1 (10 ) ;ShallowCopy obj2 = obj1; *obj2. data = 20 ; cout << *obj1. data;
3. 深拷贝的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 class DeepCopy { int * data; size_t size; public : DeepCopy (int value, size_t s = 1 ) : size (s) { data = new int [size]; for (size_t i = 0 ; i < size; ++i) { data[i] = value; } } DeepCopy (const DeepCopy& other) : size (other.size) { data = new int [size]; for (size_t i = 0 ; i < size; ++i) { data[i] = other.data[i]; } cout << "深拷贝\n" ; } DeepCopy& operator =(const DeepCopy& other) { if (this != &other) { delete [] data; size = other.size; data = new int [size]; for (size_t i = 0 ; i < size; ++i) { data[i] = other.data[i]; } } return *this ; } ~DeepCopy () { delete [] data; } void setValue (size_t index, int value) { if (index < size) data[index] = value; } int getValue (size_t index) const { return (index < size) ? data[index] : 0 ; } }; DeepCopy obj1 (10 , 5 ) ;DeepCopy obj2 = obj1; obj2. setValue (0 , 99 ); cout << obj1. getValue (0 ); cout << obj2. getValue (0 );
4. 三法则(Rule of Three):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Resource { int * data; public : ~Resource () { delete data; } Resource (const Resource& other) { data = new int (*other.data); } Resource& operator =(const Resource& other) { if (this != &other) { delete data; data = new int (*other.data); } return *this ; } };
5. 五法则(Rule of Five,C++11):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class ModernResource { int * data; public : ~ModernResource () { delete data; } ModernResource (const ModernResource& other) { data = new int (*other.data); } ModernResource& operator =(const ModernResource& other) { if (this != &other) { delete data; data = new int (*other.data); } return *this ; } ModernResource (ModernResource&& other) noexcept { data = other.data; other.data = nullptr ; } ModernResource& operator =(ModernResource&& other) noexcept { if (this != &other) { delete data; data = other.data; other.data = nullptr ; } return *this ; } };
6. 禁止拷贝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class NoCopy {public : NoCopy () = default ; NoCopy (const NoCopy&) = delete ; NoCopy& operator =(const NoCopy&) = delete ; }; class NoCopyOld {private : NoCopyOld (const NoCopyOld&); NoCopyOld& operator =(const NoCopyOld&); public : NoCopyOld () {} };
7. 实际案例对比:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class BadVector { int * arr; size_t sz; public : BadVector (size_t size) : sz (size), arr (new int [size]) {} ~BadVector () { delete [] arr; } }; void problem () { BadVector v1 (100 ) ; BadVector v2 = v1; } class GoodVector { int * arr; size_t sz; public : GoodVector (size_t size) : sz (size), arr (new int [size]) {} GoodVector (const GoodVector& other) : sz (other.sz) { arr = new int [sz]; memcpy (arr, other.arr, sz * sizeof (int )); } GoodVector& operator =(const GoodVector& other) { if (this != &other) { int * new_arr = new int [other.sz]; memcpy (new_arr, other.arr, other.sz * sizeof (int )); delete [] arr; arr = new_arr; sz = other.sz; } return *this ; } ~GoodVector () { delete [] arr; } }; void safe () { GoodVector v1 (100 ) ; GoodVector v2 = v1; }
口头解答: “拷贝构造函数在用一个对象初始化另一个对象时调用。浅拷贝和深拷贝的区别主要体现在对指针成员的处理上。浅拷贝只拷贝指针的值,结果是两个对象的指针指向同一块内存,这在析构时会出大问题——同一块内存被释放两次,程序会崩溃。深拷贝则是为新对象分配新的内存,把内容复制过去,保证每个对象有自己独立的资源。如果类里有指针、文件句柄等资源,一定要实现深拷贝。这也是’三法则’的由来:如果需要自定义析构函数,通常也需要自定义拷贝构造函数和拷贝赋值运算符。C++11又加入了移动操作,变成了’五法则’。现代C++推荐用智能指针来自动管理资源,避免手动处理这些问题。”
第一部分总结:
本部分涵盖了C++的基础知识(题目1-5)和面向对象编程(题目6-10)的核心概念:
new vs malloc、引用vs指针等基本概念
const和static关键字的多种用法
面向对象三大特性的实现
虚函数和多态机制
构造析构顺序和拷贝语义
这些是C++面试的高频考点,理解这些概念对后续学习更高级的特性至关重要。
下一部分预告: 第二部分(题目11-20)将涵盖内存管理和STL容器,包括内存分区、RAII、vector实现、迭代器等内容。