cpp考前急救

第一题

今天(18日)考试预祝大家都能顺利过关哦。

本题要求:补全下面的代码,写一个print函数,如果有参数,例如30,则输出"date=30"。如果没有传入参数,则输出今天的日期"date=18",具体格式见样例:

cpp 复制代码
// 此处填写你的代码,提交的时候,只需而且只能提交你的代码  
  
  
// 此处结束你的代码  
  
int main()  
{  
int n;  
cin>>n;  
print(n);  
print();  
return 0;  
}

可以直接使用void缺省参数:

cpp 复制代码
#include <iostream>
using namespace std;

void print(int n=18){
    cout<<"date="<<n<<endl;
}

第二题:函数重载

现有一个int数组和string类型的数组,你需要求出最小值。

提示:本题必须用函数重载。否则不得分!

函数原型:T mi(T array[],int size)

cpp 复制代码
// 此处填写你的代码,提交的时候,只需而且只能提交你的代码  
  
  
// 此处结束你的代码  
int main()  
{  
  
int i,n;  
int a[100];  
cin>>n;  
for(i=0;i<n;i++){  
cin>>a[i];  
}  
cout<<mi(a,n)<<endl;  
  
string b[100];  
cin>>n;  
for(i=0;i<n;i++){  
cin>>b[i];  
}  
cout<<mi(b,n)<<endl;  
  
return 0;  
}

输入:

第一行为整数的个数 第二行分别为各个整数 第三行为string的个数 第四行分别为各个string

![[Pasted image 20260506204322.png|697]]

关键点解析:

  1. 重载规则 :两个函数的名称都必须是 mi,返回值类型根据题目要求分别设为 intstring

  2. 字符串比较 :在 C++ 中,string 对象可以直接使用 < 运算符进行比较(基于字典序),所以两个函数的内部逻辑几乎一模一样。

  3. 注意点 :题目要求原型为 T mi(T array[], int size)。虽然这看起来很像模板(Template),但题目明确要求使用函数重载,所以我们要手动展开写出这两个具体的版本。

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

int mi(int array[],int size){
    int min=array[0];
    for(int i=0;i<size;i++){
        if (array[i]<min){
            min=arrar[i];
        }
    }
    return min;
}

string mi(string arrary[],int size){
    string min=arrary[0];
    for(int i=0;i<size;i++){
        if (array[i]<min){
            min=arrar[i];
        }
    }
    return min;
}

小综合一下:

题目:通用显示函数 display

题目要求: 补全代码,写出重载函数 display

  1. 如果传入的是一个 int ,输出 Type:Int, Value: [数值]

  2. 如果传入的是一个 double ,输出 Type:Double, Value: [数值]

  3. 如果不传参数 ,默认输出 Type:None, Value: 0

注意: 必须使用重载或缺省参数实现,确保 main 函数能正常运行。

cpp 复制代码
#include <iostream>
using namespace std;

// --- 此处填写你的代码 ---


// --- 结束填写 ---

int main() {
    int a = 10;
    double b = 3.14;

    display(a);     // 输出 Type:Int, Value: 10
    display(b);     // 输出 Type:Double, Value: 3.14
    display();      // 输出 Type:None, Value: 0

    return 0;
}

参考:

cpp 复制代码
void display(int n){
	cout<<"Type:Int,Value:"<<n<<endl;
}

void display(double m){
	cout<<"..."<<m<<endl;
}

void display(){
	cout<<"..."<<endl;
}

第三题--函数模板

程序填空题。

编写一个模板函数,计算两个数的和。本题不用模板将不得分。

cpp 复制代码
/*******************************

//此处填写你的代码,提交的时候,只需而且只能提交你的代码

*******************************/

int main() 

{  
  
int n1,n2;  
  
cin>>n1>>n2;  
cout << Add(n1, n2) << endl;  
  
double d1,d2;  
cin>>d1>>d2;  
cout << Add(d1,d2) << endl;  
  
return 0;  
}

输入:
第一行为两个整数

第二行为为2个浮点
cpp 复制代码
#include <iostream>
using namespace std;

template <typename T>
T Add(T a, T b) {
    return a + b;
}

关键点解析:

  1. template <typename T> :这是模板的声明。T 是一个占位符,代表"某种类型"。当 main 函数调用 Add(n1, n2) 时,编译器看到 n1int,就会自动把 T 替换成 int

  2. T Add(T a, T b)

    • 第一个 T 是返回值类型。

    • 括号里的 T a, T b 是参数类型。

    • 这样无论传入的是 intdouble 还是 float,只要这个类型支持 + 运算符,函数就能工作。

  3. 为什么这题比重载高级?

    • 重载 :你需要为 int 写一个函数,为 double 再写一个函数(代码逻辑重复)。

    • 模板:你只写一遍逻辑,编译器帮你生成不同版本的函数。

来道题目:

题目:通用数组查找函数 Find

题目要求: 编写一个函数模板 Find,在数组中查找指定的元素。

  1. 如果找到了,返回该元素在数组中的下标

  2. 如果没找到,返回 -1

  3. 该模板必须能同时处理 int 数组和 char 数组。

cpp 复制代码
/******************************* // 此处填写你的代码 *******************************/

int main() {
    // 测试整数数组
    int a[] = {10, 20, 30, 40, 50};
    int n;
    cin >> n; // 输入要找的整数,例如 30
    cout << Find(a, 5, n) << endl; // 应输出 2

    // 测试字符数组
    char b[] = {'a', 'b', 'c', 'd'};
    char c;
    cin >> c; // 输入要找的字符,例如 'z'
    cout << Find(b, 4, c) << endl; // 应输出 -1

    return 0;
}

解题提示:

  1. 模板声明 :你需要定义一个类型占位符 T

  2. 函数参数

    • 第一个参数是数组 T arr[]

    • 第二个参数是数组长度 int size

    • 第三个参数是要找的目标 T target

  3. 循环对比 :遍历数组,如果 arr[i] == target,立即返回 i

cpp 复制代码
#include <iostream>
using namespace std;

template <typename T>
T Find(T array[],int size,T target){
	for(int i=0;i<size;i++){
		if(array[i]==target)
			return i;
	}
	return -1;
}

如果我要求返回的不是下标,而是该元素的"两倍数值",模板该怎么写?(提示:这会涉及到返回类型的变化哦!)

如果直接是×2的话,比如a×2是会报错的,这里可以采用加法:

cpp 复制代码
template <typename T>
T DoubleIt(T n) {
    return n + n; // 巧妙用法:用 n + n 代替 n * 2

第四题--vector偶数

编写一个函数,接受一个整数vector,将该vector中所有的偶数(数值而非下标)的元素乘以2。

cpp 复制代码
// 此处填写你的代码,提交的时候,只需而且只能提交你的代码  
  
  
// 此处结束你的代码  

int main()  
{  
int n;  
cin>>n;  
int x;  
vector<int> v;  
for(int i=0;i<n;i++){  
cin>>x;  
v.push_back(x);  
}  
f(v);  
for(int i=0;i<n;i++){  
cout<<v[i]<< ' ';  
}  
cout<<endl;  
return 0;  
}

#### 输入

第一行为一个整数,表示总共有个整数输入

接下来是n个整数。

#### 输出

处理后的结果,见样例代码

#### 样例输入
5
1 2 3 4 5

#### 样例输出
1 4 3 8 5

这道题考察的是 C++ vector 的引用传递(Pass by Reference)

main 函数中,调用的是 f(v),并且在调用后还要打印 v。如果你的函数不使用引用传递,函数内部的操作将只影响副本,原本的 v 不会改变。为了让修改生效,必须在参数类型后面加一个 & 符号。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

void f(vector<int> &v){
    for(int i=0;i<v.size();i++){
        if(v[i]%2==0){
            v[i]=v[i]*2;
        }
    }
}

关键点解析:

  1. 引用传递 vector<int> &v

    • 这是本题的核心 。如果不加 &,函数会复制一份数组,你在函数里怎么乘 2,外面 main 函数里的数组都纹丝不动。
  2. v.size()

    • vector 自带 size() 方法,可以获取数组长度。
  3. 判断偶数

    • 使用取模运算 v[i] % 2 == 0 来判断元素数值是否为偶数。
  4. 乘 2 操作

    • v[i] *= 2v[i] = v[i] * 2 均可。

在 C++11 及更高版本中,也可以使用 Range-based for loop(范围遍历),代码会更简洁:

cpp 复制代码
void f(vector<int> &v) {
    for (int &x : v) { // 注意这里也要加 & 才能修改原值
        if (x % 2 == 0) {
            x *= 2;
        }
    }
}

依旧:

题目:数组的"去头去尾"与区间翻倍

题目要求: 编写一个函数 process,接受一个 vector<int> 引用以及两个整数 minmax

  1. 遍历 vector,如果元素的值在 [min, max] 闭区间内,则该元素自增 1

  2. 如果元素的值小于 min,则将其修改为 0

  3. 如果元素的值大于 max,则将其修改为 100

cpp 复制代码
/******************************* // 此处填写你的代码 *******************************/

int main() {
    int n, low, high;
    cin >> n >> low >> high; // 输入数量,以及区间的上下限
    vector<int> v;
    for(int i = 0; i < n; i++) {
        int temp;
        cin >> temp;
        v.push_back(temp);
    }

    process(v, low, high);

    for(int x : v) {
        cout << x << " ";
    }
    cout << endl;
    return 0;
}

### 样例输入
5 10 20
5 12 15 25 10

###样例输出
0 13 16 100 11

💡 核心考点提醒:

  1. 参数签名 :记得 void process(vector<int> &v, int min, int max)

  2. 逻辑顺序 :这是一个典型的 if-else if-else 结构,确保逻辑覆盖了所有情况。

  3. 引用不可丢 :只要 main 函数最后要打印修改后的 v,参数里的 & 就是你的命根子。

cpp 复制代码
#include <iostream>
#include <vector>
using namepsace std;

void process(vector<int> &v,int min,int max){
	for(int i=0;i<v.size(),i++){
		if(v[i]<min){
			v[i]=0;
		}
		else if(v[i]>max){
			v[i]=100;
		}
		else{
			v[i]++;
		}
	}
}

第五题--动态数组

read函数用于动态创建一个数组,并读取n个整数

cpp 复制代码
// 此处填写你的代码,提交的时候,只需而且只能提交你的代码



// 此处结束你的代码

int main()

{

    int n;

    cin >> n;

    int *nums = read(n);

    for(int i=0;i<n;i++){

         cout<< nums[i]<< endl;

    }

    delete [] nums;

    return 0;

}

输入
输入样例: 在这里给出一组输入。例如: 3 1 2 3

输出
输出样例: 在这里给出相应的输出。例如: 1 2 3

样例输入
3
1 2 3
样例输出
1
2
3

这道题考察的是 C++ 中的动态内存分配 (使用 new 关键字)以及指针作为函数返回值

在 C++ 中,如果你在函数内部定义一个普通的数组(如 int arr[n]),它是分配在栈上的,函数执行完就会被销毁。为了让 main 函数能继续使用这个数组,必须使用 new 在堆(Heap)上开辟空间。

cpp 复制代码
#include <iostream>
using namespace std;

int* read(int n) {
    int* p = new int[n];
    for (int i = 0; i < n; i++) {
        cin >> p[i];
    }
    return p;
}

关键点解析:

  1. 返回类型 int*

    • 函数需要返回一个指向整数数组首地址的指针。
  2. new int[n]

    • 这是动态分配的核心。它根据变量 n 的值在运行时申请内存。

    • 申请到的内存除非手动 delete[],否则在程序运行期间会一直存在,因此可以安全地返回给 main 函数使用。

  3. 循环读取

    • 使用指针 p 就像使用普通数组名一样,通过 p[i] 进行赋值。
  4. 配套说明

    • 注意到 main 函数末尾有 delete [] nums;,这正好对应了我们代码里的 new int[n],保证了内存不会泄漏。

依旧:

题目:动态创建并初始化"倍数数组"

题目要求: 编写一个函数 createMultiples,要求:

  1. 接受两个参数:数组长度 n 和倍数基数 base

  2. 在函数内部动态创建一个长度为 nint 数组

  3. 将数组初始化为:base * 1, base * 2, ..., base * n

  4. 返回该数组的首地址。

cpp 复制代码
/******************************* // 此处填写你的代码 *******************************/

int main() {
    int size, k;
    cin >> size >> k;

    // 调用你的函数
    int* myArr = createMultiples(size, k);

    // 输出验证
    for(int i = 0; i < size; i++) {
        cout << myArr[i] << (i == size - 1 ? "" : " ");
    }
    cout << endl;

    // 释放内存
    delete[] myArr;
    return 0;
}

###样例输入
4 5

###样例输出
5 10 15 20

核心考点:

  1. 函数头定义 : 返回类型必须是 int*,因为你要把那一串房子的"大门钥匙"交给 main 函数。

  2. new 的语法int* p = new int[n];。记住 new 后面跟类型,方括号里跟长度。

  3. 循环赋值 : 下标是从 0n-1。根据题意,第 i 个元素的值应该是 base * (i + 1)

cpp 复制代码
 int* creatMultiples(int n,int base){
 	int* p=new int[n];
 	for(int i=0;i<n;i++){
	 	p[i]=base*(i+1);
	 }
	 return p;
 }

再来一道,这道题考察的是动态内存的按需分配 :你需要先根据输入计算出需要多大的空间,再进行申请,并且涉及到指针的偏移操作

题目:动态创建"过滤"数组

题目要求:

编写一个函数 filterAndCopy,要求:

  1. 接受一个原始 int 数组 arr 和它的长度 n,以及一个阈值 threshold

  2. 统计原数组中大于 threshold 的元素个数 mmm。

  3. 动态创建一个长度为 mmm 的新数组 。如果 m=0m=0m=0, 返回 nullptr(空指针)。

  4. 将原数组中所有大于 threshold 的元素按顺序拷贝到新数组中。

  5. 通过引用参数 newSize 将新数组的长度 mmm 传回给调用者。

  6. 返回新数组的首地址。

cpp 复制代码
/******************************* 
// 此处填写你的代码
*******************************/

int main() {
    int n, limit;
    cin >> n >> limit;
    int* data = new int[n];
    for(int i = 0; i < n; i++) cin >> data[i];

    int m = 0;
    // 调用你的函数
    int* result = filterAndCopy(data, n, limit, m);

    if (result == nullptr) {
        cout << "None" << endl;
    } else {
        cout << m << endl;
        for(int i = 0; i < m; i++) {
            cout << result[i] << (i == m - 1 ? "" : " ");
        }
        cout << endl;
        delete[] result; // 释放新数组
    }

    delete[] data; // 释放原数组
    return 0;
}

💡 核心考点:

  1. 两次遍历 :你需要先遍历一遍统计个数,确定 new 的大小;再遍历一遍进行拷贝。

  2. 引用传参int &newSize 允许你在函数内修改 main 函数里的变量 m

  3. 空指针处理 :如果没有符合条件的元素,按照 C++ 规范应当返回 nullptr

cpp 复制代码
int* filterAndCopy(int arr[], int n, int threshold, int &newSize) {
    // 1. 统计个数
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (arr[i] > threshold) count++;
    }
    
    // 2. 更新传回的长度
    newSize = count;
    
    // 3. 处理没有符合条件的情况
    if (count == 0) return nullptr;
    
    // 4. 动态申请新内存
    int* newArr = new int[count];
    
    // 5. 拷贝数据
    int j = 0; // 新数组的下标
    for (int i = 0; i < n; i++) {
        if (arr[i] > threshold) {
            newArr[j] = arr[i];
            j++;
        }
    }
    
    return newArr;
}

第六题--动态数组扩容

一旦数组创建后,其大小就是固定的了。有时,需要向数组中加入更多的数据。但数组已经满了。在这种情况下,需要创建一个新的数组,代替当前数组。编写一个函数,函数头如下:
int* doubleCapacity(const int* list, int size, int n)

函数返回一个新的数组,大小是原数组list的n倍。

为简化操作,假设原数组size为5。

为测试需要,在函数内原有数据不变,其他数据赋值为下标,例如:list[8]=8;

cpp 复制代码
测试main函数如下:

/*****************************************

 此处填写你的代码,只需而且只能提交你的代码 

*****************************************/

int main()

{

int list[5];

for (int i = 0; i < 5; i++)

cin>>list[i];

int n;

cin>>n;

int* newList = doubleCapacity(list, 5,n);

for (int i = 0; i < n * 5; i++)

cout << *(newList + i) << " ";

return 0;

}

#输入
第一行为5个int,表示原有数组的数据。
第二行为n,表示新开的数组大小是原来的n倍

#输出
输出新数组的所有元素值。

###样例输入
10 20 30 40 50
2

###样例输入
10 20 30 40 50 5 6 7 8 9
cpp 复制代码
int* doubleCapacity(const int* list, int size, int n){
	int newSize=size*n;
	
	int* newList=new int[newSize];
	for(int i=0;i<size;i++){
		newList[i]=list[i];
	}
	for(int i=size;i<newSize;i++){
		newList[i]=i;
	}
	return newList;
}

关键逻辑拆解:

  1. 动态扩容的核心

    • C++ 的普通数组(如 main 中的 list[5])大小固定,无法改变。

    • 我们要想"变大",必须用 new 申请一块更大的内存,把旧数据搬过去,这就是 vector 等容器底层的基本原理。

  2. 两个循环的作用

    • 第一个循环 :负责"搬家",把旧数组 list 里的前 5 个数原封不动拷贝到新家 newList

    • 第二个循环 :负责"装修",从下标 5 开始直到 n*5,按照题目要求给新出的空间赋值(newList[i] = i)。

  3. 指针返回

    • 函数返回 int*,这样 main 函数里的 newList 指针就能接管这块在堆上申请的新内存。

依旧:

题目:动态截取数组 subArray

题目要求: 编写一个函数 subArray,从原数组中截取一段特定的区间,并存入新创建的动态数组中。

  1. 参数:const int* list(原数组)、int start(起始下标)、int end(结束下标)。

  2. 逻辑:创建一个新数组,大小正好能装下从 startend(包含两端)的所有元素。

  3. 如果 start > end 或者下标越界(假设原数组最大长度为 100),返回 nullptr

  4. 返回新数组的首地址。

核心考点:

  • 长度计算 :截取 [start, end] 区间,新数组的长度应该是 end - start + 1

  • 偏移量拷贝 :拷贝时,原数组的下标是 i,新数组的下标是从 0 开始。

cpp 复制代码
int main() {
    int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int s, e;
    cin >> s >> e; // 输入 2 5

    int* sub = subArray(arr, s, e);

    if (sub != nullptr) {
        int length = e - s + 1;
        for (int i = 0; i < length; i++) {
            cout << sub[i] << " ";
        }
        delete[] sub;
    }
    return 0;
}
cpp 复制代码
int* subArray(const int* list, int start, int end) {
    // 基础合法性检查:起始不能比结束大,且不能为负数
    if (start > end || start < 0) {
        return nullptr;
    }
    
    // 计算新数组的大小
    int size = end - start + 1;
    
    // 动态申请空间
    int* arr = new int[size];
    
    // 拷贝数据:i 是新数组下标,start + i 是原数组下标
    for (int i = 0; i < size; i++) {
        arr[i] = list[start + i];
    }
    
    return arr;
}

第七题--静态成员变量

Circle类中创建1个静态成员变量,表示创建的对象个数;1个静态成员函数,返回当前对象的个数。

构造函数中会增加对象个数,析构函数中减少对象个数。

cpp 复制代码
// 此处填写你的代码,提交的时候,只需而且只能提交你的代码

// 此处结束你的代码


int main()

{

 Circle *p1=new Circle();

 cout<<Circle::getNums()<<endl;

 Circle *p2=new Circle();

 cout<<Circle::getNums()<<endl;

 Circle *p3=new Circle();

 cout<<Circle::getNums()<<endl;

 delete p1;

 cout<<Circle::getNums()<<endl;

 delete p2;

 cout<<Circle::getNums()<<endl;

 delete p3;

 cout<<Circle::getNums()<<endl;

 return 0;

}

静态成员变量不属于某个具体的对象,而是属于整个类,由所有对象共享。这正是用来"计数"的最佳工具。

cpp 复制代码
class Circle {
private:
    static int nums; // 声明静态变量,记录个数

public:
    Circle() {
        nums++; // 每创建一个对象,计数加 1
    }
    
    ~Circle() {
        nums--; // 每销毁一个对象,计数减 1
    }

    static int getNums() {
        return nums; // 静态成员函数,只能访问静态变量
    }
};

// 必须在类外对静态成员变量进行初始化
int Circle::nums = 0;

关键点解析:

  1. static int nums

    • 在类内只是声明

    • 必须在类外进行定义和初始化int Circle::nums = 0;),否则编译器会报错(未定义的引用)。

  2. static int getNums()

    • 静态成员函数可以通过类名直接调用(如 Circle::getNums()),不需要对象。

    • 它内部不能使用 this 指针,也不能访问非静态成员。

  3. 构造与析构

    • new Circle() 时触发构造函数,nums 自增。

    • delete p1 时触发析构函数,nums 自减。

    • 这样就能实时监控内存中存在的对象数量。

依旧:

这道题考察的是静态成员在"全局资源管理"中的实际应用。在实际开发中,我们经常需要给每一个对象分配一个唯一的、递增的 ID,这通常就需要借助静态变量来实现。

题目:带唯一 ID 的 Student 类

题目要求: 编写一个 Student 类,要求:

  1. 静态成员变量totalStudents(记录当前存活的对象个数)和 idCounter(用于生成下一个可用的 ID,初始值为 1000)。

  2. 普通成员变量id(每个学生特有的唯一编号)。

  3. 构造函数

    • totalStudents 加 1。

    • 将当前的 idCounter 赋值给该对象的 id,然后 idCounter 自增 1。

  4. 析构函数totalStudents 减 1。

  5. 静态成员函数getTotal() 返回当前存活的学生人数。

  6. 普通成员函数getId() 返回该学生的唯一编号。

cpp 复制代码
int main() {
    cout << "Total: " << Student::getTotal() << endl; // 应输出 0

    Student *s1 = new Student();
    Student *s2 = new Student();
    
    cout << "S1 ID: " << s1->getId() << endl;      // 应输出 1000
    cout << "S2 ID: " << s2->getId() << endl;      // 应输出 1001
    cout << "Total: " << Student::getTotal() << endl; // 应输出 2

    delete s1;
    cout << "Total after delete: " << Student::getTotal() << endl; // 应输出 1

    return 0;
}

💡 核心考点提醒:

  1. 区分"总数"与"计数器"totalStudents 会随着析构函数减少,但 idCounter 只能增加,不能减少(否则 ID 会重复)。

  2. 类外初始化:两个静态变量都需要在类外初始化。

  3. 唯一性逻辑:在构造函数中完成 ID 的分配。

cpp 复制代码
class Student {
private:
    int id;                    // 实例变量:每个对象一份
    static int totalStudents;  // 静态变量:所有对象共享(存活总数)
    static int idCounter;      // 静态变量:所有对象共享(分配器)
public:
    Student() {
        totalStudents++;
        id = idCounter++;      // 先赋值,再自增
    }
    ~Student() {
        totalStudents--;
    }
    int getId() {
        return id;
    }
    static int getTotal() {
        return totalStudents;
    }
};
// 类外初始化
int Student::totalStudents = 0;
int Student::idCounter = 1000;

第八题--类与对象综合

编写一个圆的类 Circle ,有1个私有数据成员:半径r,(r为int类型)

公有成员有:

(1)带默认形参值(默认值为1)的构造函数,同时输出:"construct:x"(x替换为半径r,不包括双引号,输出后需换行,下同)

(2)析构函数:输出:"destruct:x"

(3)拷贝构造函数,我们的拷贝构造函数有一个神奇的特性,每次拷贝,r都会翻倍。

PI取值为整数3。

cpp 复制代码
// 此处结束你的代码
int main()

{
int x;
cin>>x;
Circle a;
Circle b(x);
Circle c(b);
cout<<c.getArea()<<endl;
return 0;
}

输入
b的半径
输出
见样例。输出内容无空格。
###样例输入
4

###样例输出
construct:1
construct:4
192
destruct:8
destruct:4
destruct:1

这道题是考察 C/C++ 类与对象 的综合练习。它涵盖了带默认参数的构造函数、析构函数、拷贝构造函数(含特殊逻辑)以及成员函数。

最需要注意的坑是拷贝构造函数的执行顺序 :在 main 中,Circle c(b); 会触发拷贝构造。由于 c 是由 b 拷贝而来的,根据题目要求,c 的半径应该是 b 的半径的两倍。

cpp 复制代码
class Circle {
private:
    int r;

public:
    // (1) 带默认参数的构造函数
    Circle(int radius = 1) {
        r = radius;
        cout << "construct:" << r << endl;
    }

    // (2) 析构函数
    ~Circle() {
        cout << "destruct:" << r << endl;
    }

    // (3) 拷贝构造函数:半径翻倍
    Circle(const Circle &other) {
        r = other.r * 2;
        // 注意:题目没要求拷贝构造时输出 construct,所以这里保持沉默
    }

    // 计算面积:PI * r * r,PI 取 3
    int getArea() {
        return 3 * r * r;
    }
};

关键点拨:

  1. 拷贝构造函数的逻辑

    • 题目说"每次拷贝,r 都会翻倍"。这意味着在新对象 c 的构造过程中,它的 r 应该是被拷贝对象 br 的 2 倍。

    • 验证样例 :输入 4

      • Circle b(x):半径是 4。

      • Circle c(b):触发拷贝,c 的半径变为 4 * 2 = 8

      • c.getArea():3×8×8=3×64=1923 \times 8 \times 8 = 3 \times 64 = 1923×8×8=3×64=192。完全匹配样例!

  2. 析构顺序(先进后出)

    • 栈上的对象销毁顺序与创建顺序相反。

    • 创建顺序:a(1) -> b(4) -> c(8)

    • 析构顺序:c(8) -> b(4) -> a(1)。这解释了样例输出最后的析构序列。

  3. 默认参数

    • Circle(int radius = 1) 这种写法,使得 Circle a; 调用时会自动补上 1

🧠 考前"拷贝构造"急救知识:

  • 什么时候触发拷贝构造?

    1. 用一个对象初始化另一个对象时(如 Circle c = b;Circle c(b);)。

    2. 对象作为函数参数值传递时。

    3. 函数返回一个对象时。

  • 参数为什么要加 &

    • 必须是引用传递(const Circle &other)。如果值传递,会陷入"为了拷贝而调用拷贝构造"的无限死循环。

最后检查一下:

  • 题目要求 PI 取 3。

  • 构造和析构输出冒号后没有空格。

  • 成员函数名是 getArea()

题目:带计数的"变色龙"类 Color

题目要求: 编写一个 Color 类,包含一个私有成员 int value

  1. 构造函数 :接受一个 int 参数(默认值为 0),输出 "base:v"(v 为 value 值),并换行。

  2. 拷贝构造函数 :拷贝时,新对象的 value 等于原对象的 value + 1 。输出 "copy:v"(v 为新对象的 value 值),并换行。

  3. 析构函数 :输出 "end:v",并换行。

cpp 复制代码
// 模拟一个函数,参数是"值传递"
void show(Color c) {
    // 此时会触发一次拷贝构造
}

int main() {
    Color c1(10);    // 构造 c1
    Color c2(c1);    // 拷贝构造 c2
    show(c2);        // 调用函数,参数传递触发拷贝构造
    
    return 0;
}

预期的输出逻辑顺序(脑补一下):

  1. base:10 (创建 c1)

  2. copy:11 (由 c1 拷贝出 c2,10+1=11)

  3. copy:12 (将 c2 传给函数 show 时的临时拷贝,11+1=12)

  4. end:12 (函数 show 结束,临时对象销毁)

  5. end:11 (main 结束,c2 销毁)

  6. end:10 (main 结束,c1 销毁)

cpp 复制代码
class Color {
private:
    int value;
public:
    // 构造函数
    Color(int v = 0) : value(v) {
        cout << "base:" << value << endl;
    }

    // 拷贝构造函数
    Color(const Color &other) {
        value = other.value + 1;
        cout << "copy:" << value << endl;
    }

    // 析构函数
    ~Color() {
        cout << "end:" << value << endl;
    }
};

💡 考前最后的"排雷"手册:

1. 为什么 show(c2) 会输出 copy:12

这是考试最爱考的逻辑:当函数参数是 void show(Color c)不是 void show(Color &c) 时,系统会调用拷贝构造函数创建一个临时对象。这个临时对象在函数结束时就会被立即析构。

2. 析构的"后进先出" (LIFO)

在同一个作用域(比如 main)里,先定义的后销毁。但在上面的例子中,函数 show 内部的对象是最先销毁的,因为它所在的"小房子"(函数作用域)先拆迁了。

3. 初始化列表语法

答案里用了 : value(v),这叫初始化列表 ,在构造函数里比 value = v 效率更高,也是 C++ 推荐的标准写法。


🏁 考前总动员

现在已经把 C++ 考研/校考中最难缠的几个点都过了一遍:

  • 指针与动态内存newdelete[]、返回指针。

  • 引用与传递& 符号在参数、返回值、拷贝构造中的应用。

  • 面向对象:构造、析构、静态成员、拷贝构造。

  • 模板与重载:代码的复用性。

考场秘籍:

  • 读题三遍 :看清是 int 还是 double,看清输出有没有空格。

  • 不要慌张:遇到不会的指针操作,画个简单的图,看看谁指着谁。

  • 检查符号;}:: 是不是都写对了。

相关推荐
Byron Loong2 小时前
【基础】c,c++编译过程
c语言·c++
纪伊路上盛名在2 小时前
机器学习中常见的距离度量函数 Distance metrics
人工智能·算法·机器学习·数据分析·统计
sheeta19982 小时前
LeetCode 每日一题笔记 日期:2026.05.07 题目:3660. 找到所有可以到达的最大值
笔记·算法·leetcode
经济元宇宙2 小时前
哪款工业仿真软件上手简单?企业常用款推荐
人工智能·算法
Hesionberger2 小时前
LeetCode79:单词搜索DFS回溯详解
java·开发语言·c++·python·算法·leetcode·c#
纪伊路上盛名在3 小时前
聊一聊关于gene的富集分析
算法·数据分析·统计分析·计算生物·gene
诙_3 小时前
C++数据结构--AVL树
数据结构
MZ_ZXD0013 小时前
springboot音乐播放器系统-计算机毕业设计源码76317
java·c语言·c++·spring boot·python·flask·php
米粒13 小时前
力扣算法刷题 Day 62 最短路算法
算法·leetcode·职场和发展