C++复试笔记(五)
1.getline()的使用
在C++中,getline
是一个非常有用的函数,用于从输入流(如标准输入或文件流)读取一行文本。它能够读取包含空格的字符串,直到遇到换行符为止。getline
函数有几种不同的使用方式,具体取决于您想要从哪里读取数据。
基本用法
-
从标准输入读取:
#include <iostream> #include <string> int main() { std::string line; std::cout << "请输入一行文本: "; std::getline(std::cin, line); std::cout << "您输入的是: " << line << std::endl; return 0; }
在这个例子中,
std::getline(std::cin, line);
会从标准输入读取一行,并将其存储在line
变量中,直到用户按下回车键。 -
从文件读取:
如果要从文件中读取一行,可以这样做:
#include <iostream> #include <fstream> #include <string> int main() { std::ifstream file("example.txt"); std::string line; if (file.is_open()) { while (std::getline(file, line)) { std::cout << line << std::endl; } file.close(); } else { std::cerr << "无法打开文件" << std::endl; } return 0; }
这里,
std::getline(file, line)
逐行读取文件内容,并将每一行存储在line
变量中。
注意事项
-
getline
默认以换行符作为结束符,但是可以通过第三个参数自定义分隔符。 -
使用
getline
读取字符串时,如果之前使用了>>
操作符来读取数据,注意可能需要先清除缓冲区中的换行符,例如通过std::cin.ignore()
来忽略掉剩余的换行符,避免影响后续的getline
调用。
2.希尔排序
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
简单来说,就是分组进行排序,把数组里的元素分成不同的组进行排序(这种排序的内核就是插入排序),刚开始时gap(gap指的是组中元素的间距)比较大,让数组更加有序,最后当gap=1时(此时就是插入),数组已经接近有序,在进行插入排序可以节省大量的时间,时间复杂度大大降低
代码如下:
// 希尔排序
void ShellSort(int* a, int n)
{
int gap = n;
int i = 0;
while (gap > 0)
{
gap /= 2;
for (i = 0; i < n - gap; i++)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
希尔排序的特性总结:
希尔排序是对直接插入排序的优化。当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定
时间复杂度平均:O(N^1.3)
空间复杂度:O(1)稳定性:不稳定
3.友元函数
关于友元函数的说法错误的是()
A.友元函数的声明必须在类内 B.友元函数可以是类的成员函数
C.友元函数可以访问类的私有成员 D.类与类直接的友元函数不能直接继承
答案:B
A. 友元函数的声明必须在类内
这是正确的。为了使一个函数成为某个类的友元,该函数的声明必须出现在类定义内部,并且使用friend
关键字进行声明。B. 友元函数可以是类的成员函数
这个说法是错误的。友元函数不是类的成员函数。友元函数是在类外部定义的普通函数或另一个类的成员函数,但它们通过friend
声明获得了访问该类私有和保护成员的权限。因此,友元函数与成员函数是不同的概念。C. 友元函数可以访问类的私有成员
这是正确的。友元函数的主要特性之一就是它可以访问类的私有成员(包括私有数据成员和私有成员函数)以及保护成员。D. 类与类直接的友元函数不能直接继承
这也是正确的。如果一个类是另一个类的友元,则这种友元关系不会自动被派生类继承。也就是说,派生类默认情况下并不能访问基类的私有成员,除非它也被明确声明为友元。
4.static
储存类型为 static 的变量可以是()
A.只可以是局部变量,不可以是全局变量 B.不可以是局部变量,只可以是全局变量
C.可以是局部变量,也可以是全局变量 D.既不可以是局部变量,也不也可以是全局变量
正确的选项是:
C. 可以是局部变量,也可以是全局变量
-
静态局部变量:当
static
用于函数内部声明的变量时,它意味着该变量的生命周期贯穿整个程序运行期间,但其作用域仅限于声明它的函数内部。这意味着静态局部变量在函数调用之间保持其值,并且只在第一次进入函数时初始化。void func() { static int count = 0; // 静态局部变量 count++; std::cout << count << std::endl; }
-
静态全局变量:当
static
用于文件中的全局变量声明时,它限制了该变量的作用域到声明它的源文件内,即具有内部链接性。这意味着即使在同一程序中的其他文件也不能访问这个静态全局变量。static int globalVar = 10; // 静态全局变量,仅在此文件中可访问
因此,
static
关键字可以应用于局部变量和全局变量,分别赋予它们不同的特性:对于局部变量,延长其生命周期;对于全局变量,限制其作用域到声明它的文件。
5.const/整形(非整形)/static
1. 类内初始化规则
-
静态
const
成员:-
整型或枚举类型:允许直接在类内初始化(C++03起支持)。
static const int s_ci = 10; // ✅ 正确
-
非整型(如
double
):必须在类外定义和初始化(C++03起)。static const double s_cd; // 类内声明 const double MyClass::s_cd = 3.14; // 类外初始化 ✅
-
-
非静态
const
成员:-
C++11前:必须通过构造函数的初始化列表初始化。
const int ci; // 类内声明 MyClass(int val) : ci(val) {} // ✅ 初始化列表
-
C++11及之后:支持在类内直接初始化。
const int ci = 42; // ✅ 类内初始化(C++11特性) const double value = 3.14;
-
6.简易时钟
要在C++中实现一个简易时钟类,该类包含时、分、秒,并且每秒钟自动刷新时间并处理进位(如秒达到60则分钟加1,分钟达到60则小时加1等),你可以使用标准库中的<chrono>
和<thread>
来帮助实现计时和线程睡眠功能。以下是一个简单的实现:
#include <iostream>
#include <chrono>
#include <thread>
class SimpleClock {
public:
SimpleClock(int hour = 0, int minute = 0, int second = 0)
: h(hour), m(minute), s(second) {}
void tick() {
while (true) { // 无限循环
displayTime(); // 显示当前时间
std::this_thread::sleep_for(std::chrono::seconds(1)); // 等待一秒
++s; // 秒数加1
normalizeTime(); // 检查并调整时间
}
}
private:
int h, m, s;
void displayTime() const {
std::cout << (h < 10 ? "0" : "") << h << ":"
<< (m < 10 ? "0" : "") << m << ":"
<< (s < 10 ? "0" : "") << s << "\r";
std::cout.flush(); // 刷新输出缓冲区,确保即时显示
}
void normalizeTime() {
if (s >= 60) {
s -= 60;
m++;
}
if (m >= 60) {
m -= 60;
h++;
}
if (h >= 24) {
h -= 24;
}
}
};
int main() {
SimpleClock clock(23, 59, 55); // 示例: 设置为接近午夜的时间
// 使用新线程运行tick函数,避免阻塞main函数
std::thread clockThread(&SimpleClock::tick, &clock);
// 确保主线程等待子线程结束(实际上这个例子中不会结束)
clockThread.join();
return 0;
}
关键点解释:
- 成员变量:
h
,m
,s
分别代表小时、分钟和秒。 - 构造函数:允许用户在创建对象时指定初始时间,默认为00:00:00。
tick
方法:负责每秒钟更新时间并调用normalizeTime
方法进行进位处理,同时显示当前时间。displayTime
方法:格式化输出当前时间,确保秒、分、时均为两位数显示。- 使用
std::cout.flush()
立即刷新输出缓冲区,确保即时显示。 normalizeTime
方法:检查是否需要对秒、分、时进行进位调整。
注意,在这个示例中,tick
方法会无限循环执行。实际应用中你可能需要增加条件来控制何时停止计时器。此外,我们使用了C++11的线程库来创建一个新的线程运行tick
方法,这样可以避免阻塞主程序流程。std::this_thread::sleep_for
用于让当前线程暂停一秒钟,模拟时钟的一秒间隔。