第一题
今天(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]]
关键点解析:
-
重载规则 :两个函数的名称都必须是
mi,返回值类型根据题目要求分别设为int和string。 -
字符串比较 :在 C++ 中,
string对象可以直接使用<运算符进行比较(基于字典序),所以两个函数的内部逻辑几乎一模一样。 -
注意点 :题目要求原型为
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。
-
如果传入的是一个
int,输出Type:Int, Value: [数值]。 -
如果传入的是一个
double,输出Type:Double, Value: [数值]。 -
如果不传参数 ,默认输出
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;
}
关键点解析:
-
template <typename T>:这是模板的声明。T是一个占位符,代表"某种类型"。当main函数调用Add(n1, n2)时,编译器看到n1是int,就会自动把T替换成int。 -
T Add(T a, T b):-
第一个
T是返回值类型。 -
括号里的
T a, T b是参数类型。 -
这样无论传入的是
int、double还是float,只要这个类型支持+运算符,函数就能工作。
-
-
为什么这题比重载高级?
-
重载 :你需要为
int写一个函数,为double再写一个函数(代码逻辑重复)。 -
模板:你只写一遍逻辑,编译器帮你生成不同版本的函数。
-
来道题目:
题目:通用数组查找函数 Find
题目要求: 编写一个函数模板 Find,在数组中查找指定的元素。
-
如果找到了,返回该元素在数组中的下标。
-
如果没找到,返回
-1。 -
该模板必须能同时处理
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;
}
解题提示:
-
模板声明 :你需要定义一个类型占位符
T。 -
函数参数:
-
第一个参数是数组
T arr[]。 -
第二个参数是数组长度
int size。 -
第三个参数是要找的目标
T target。
-
-
循环对比 :遍历数组,如果
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;
}
}
}
关键点解析:
-
引用传递
vector<int> &v:- 这是本题的核心 。如果不加
&,函数会复制一份数组,你在函数里怎么乘 2,外面main函数里的数组都纹丝不动。
- 这是本题的核心 。如果不加
-
v.size():vector自带size()方法,可以获取数组长度。
-
判断偶数:
- 使用取模运算
v[i] % 2 == 0来判断元素数值是否为偶数。
- 使用取模运算
-
乘 2 操作:
v[i] *= 2或v[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> 引用以及两个整数 min 和 max。
-
遍历
vector,如果元素的值在[min, max]闭区间内,则该元素自增 1。 -
如果元素的值小于
min,则将其修改为0。 -
如果元素的值大于
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
💡 核心考点提醒:
-
参数签名 :记得
void process(vector<int> &v, int min, int max)。 -
逻辑顺序 :这是一个典型的
if-else if-else结构,确保逻辑覆盖了所有情况。 -
引用不可丢 :只要
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;
}
关键点解析:
-
返回类型
int*:- 函数需要返回一个指向整数数组首地址的指针。
-
new int[n]:-
这是动态分配的核心。它根据变量
n的值在运行时申请内存。 -
申请到的内存除非手动
delete[],否则在程序运行期间会一直存在,因此可以安全地返回给main函数使用。
-
-
循环读取:
- 使用指针
p就像使用普通数组名一样,通过p[i]进行赋值。
- 使用指针
-
配套说明:
- 注意到
main函数末尾有delete [] nums;,这正好对应了我们代码里的new int[n],保证了内存不会泄漏。
- 注意到
依旧:
题目:动态创建并初始化"倍数数组"
题目要求: 编写一个函数 createMultiples,要求:
-
接受两个参数:数组长度
n和倍数基数base。 -
在函数内部动态创建一个长度为
n的int数组。 -
将数组初始化为:
base * 1, base * 2, ..., base * n。 -
返回该数组的首地址。
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
核心考点:
-
函数头定义 : 返回类型必须是
int*,因为你要把那一串房子的"大门钥匙"交给main函数。 -
new的语法 :int* p = new int[n];。记住new后面跟类型,方括号里跟长度。 -
循环赋值 : 下标是从
0到n-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,要求:
-
接受一个原始
int数组arr和它的长度n,以及一个阈值threshold。 -
统计原数组中大于
threshold的元素个数 mmm。 -
动态创建一个长度为 mmm 的新数组 。如果 m=0m=0m=0, 返回
nullptr(空指针)。 -
将原数组中所有大于
threshold的元素按顺序拷贝到新数组中。 -
通过引用参数
newSize将新数组的长度 mmm 传回给调用者。 -
返回新数组的首地址。
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;
}
💡 核心考点:
-
两次遍历 :你需要先遍历一遍统计个数,确定
new的大小;再遍历一遍进行拷贝。 -
引用传参 :
int &newSize允许你在函数内修改main函数里的变量m。 -
空指针处理 :如果没有符合条件的元素,按照 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;
}
关键逻辑拆解:
-
动态扩容的核心:
-
C++ 的普通数组(如
main中的list[5])大小固定,无法改变。 -
我们要想"变大",必须用
new申请一块更大的内存,把旧数据搬过去,这就是vector等容器底层的基本原理。
-
-
两个循环的作用:
-
第一个循环 :负责"搬家",把旧数组
list里的前 5 个数原封不动拷贝到新家newList。 -
第二个循环 :负责"装修",从下标
5开始直到n*5,按照题目要求给新出的空间赋值(newList[i] = i)。
-
-
指针返回:
- 函数返回
int*,这样main函数里的newList指针就能接管这块在堆上申请的新内存。
- 函数返回
依旧:
题目:动态截取数组 subArray
题目要求: 编写一个函数 subArray,从原数组中截取一段特定的区间,并存入新创建的动态数组中。
-
参数:
const int* list(原数组)、int start(起始下标)、int end(结束下标)。 -
逻辑:创建一个新数组,大小正好能装下从
start到end(包含两端)的所有元素。 -
如果
start > end或者下标越界(假设原数组最大长度为 100),返回nullptr。 -
返回新数组的首地址。
核心考点:
-
长度计算 :截取
[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;
关键点解析:
-
static int nums:-
在类内只是声明。
-
必须在类外进行定义和初始化 (
int Circle::nums = 0;),否则编译器会报错(未定义的引用)。
-
-
static int getNums():-
静态成员函数可以通过类名直接调用(如
Circle::getNums()),不需要对象。 -
它内部不能使用
this指针,也不能访问非静态成员。
-
-
构造与析构:
-
new Circle()时触发构造函数,nums自增。 -
delete p1时触发析构函数,nums自减。 -
这样就能实时监控内存中存在的对象数量。
-
依旧:
这道题考察的是静态成员在"全局资源管理"中的实际应用。在实际开发中,我们经常需要给每一个对象分配一个唯一的、递增的 ID,这通常就需要借助静态变量来实现。
题目:带唯一 ID 的 Student 类
题目要求: 编写一个 Student 类,要求:
-
静态成员变量 :
totalStudents(记录当前存活的对象个数)和idCounter(用于生成下一个可用的 ID,初始值为 1000)。 -
普通成员变量 :
id(每个学生特有的唯一编号)。 -
构造函数:
-
totalStudents加 1。 -
将当前的
idCounter赋值给该对象的id,然后idCounter自增 1。
-
-
析构函数 :
totalStudents减 1。 -
静态成员函数 :
getTotal()返回当前存活的学生人数。 -
普通成员函数 :
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;
}
💡 核心考点提醒:
-
区分"总数"与"计数器" :
totalStudents会随着析构函数减少,但idCounter只能增加,不能减少(否则 ID 会重复)。 -
类外初始化:两个静态变量都需要在类外初始化。
-
唯一性逻辑:在构造函数中完成 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;
}
};
关键点拨:
-
拷贝构造函数的逻辑:
-
题目说"每次拷贝,r 都会翻倍"。这意味着在新对象
c的构造过程中,它的r应该是被拷贝对象b的r的 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。完全匹配样例!
-
-
-
析构顺序(先进后出):
-
栈上的对象销毁顺序与创建顺序相反。
-
创建顺序:
a(1)->b(4)->c(8)。 -
析构顺序:
c(8)->b(4)->a(1)。这解释了样例输出最后的析构序列。
-
-
默认参数:
Circle(int radius = 1)这种写法,使得Circle a;调用时会自动补上1。
🧠 考前"拷贝构造"急救知识:
-
什么时候触发拷贝构造?
-
用一个对象初始化另一个对象时(如
Circle c = b;或Circle c(b);)。 -
对象作为函数参数值传递时。
-
函数返回一个对象时。
-
-
参数为什么要加
&?- 必须是引用传递(
const Circle &other)。如果值传递,会陷入"为了拷贝而调用拷贝构造"的无限死循环。
- 必须是引用传递(
最后检查一下:
-
题目要求 PI 取 3。
-
构造和析构输出冒号后没有空格。
-
成员函数名是
getArea()。
题目:带计数的"变色龙"类 Color
题目要求: 编写一个 Color 类,包含一个私有成员 int value。
-
构造函数 :接受一个
int参数(默认值为 0),输出"base:v"(v 为 value 值),并换行。 -
拷贝构造函数 :拷贝时,新对象的
value等于原对象的value + 1。输出"copy:v"(v 为新对象的 value 值),并换行。 -
析构函数 :输出
"end:v",并换行。
cpp
// 模拟一个函数,参数是"值传递"
void show(Color c) {
// 此时会触发一次拷贝构造
}
int main() {
Color c1(10); // 构造 c1
Color c2(c1); // 拷贝构造 c2
show(c2); // 调用函数,参数传递触发拷贝构造
return 0;
}
预期的输出逻辑顺序(脑补一下):
-
base:10(创建 c1) -
copy:11(由 c1 拷贝出 c2,10+1=11) -
copy:12(将 c2 传给函数show时的临时拷贝,11+1=12) -
end:12(函数show结束,临时对象销毁) -
end:11(main 结束,c2 销毁) -
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++ 考研/校考中最难缠的几个点都过了一遍:
-
指针与动态内存 :
new、delete[]、返回指针。 -
引用与传递 :
&符号在参数、返回值、拷贝构造中的应用。 -
面向对象:构造、析构、静态成员、拷贝构造。
-
模板与重载:代码的复用性。
考场秘籍:
-
读题三遍 :看清是
int还是double,看清输出有没有空格。 -
不要慌张:遇到不会的指针操作,画个简单的图,看看谁指着谁。
-
检查符号 :
;、}、::是不是都写对了。