139. 如果派生类中存在与基类相同的函数,那么调用派生类的函数。
在通常情况下,如果派生类中存在与基类相同的函数,并且没有特别指定,那么调用派生类的函数。这是因为在C++中,派生类会覆盖(隐藏)基类中具有相同签名的函数。
140. 若指针px为空指针,则A.px的地址为零 B.px指向不定 C.px的值为零 D.px的目标为零
正确答案是:
C. px的值为零
解释:
- **空指针(null pointer)是指一个特殊的指针,它不指向任何有效的内存地址。通常在 C 和 C++ 中,空指针的值为
0
或nullptr
(在现代 C++ 中)。
int* px = nullptr; // px 是一个空指针
在这里,px
不指向任何有效的内存位置,其值为 0
或 nullptr
。
其他选项解释:
-
A. px的地址为零:这个说法不准确。
px
是一个指针变量,它本身存储在某个内存地址中,而它的值是0
。所以,指针px
的值为零,而不是指针变量px
的地址为零。 -
B. px指向不定:空指针有明确的值(通常是
0
或nullptr
),并且它明确表示不指向任何有效的内存地址。不定指针意味着指针包含随机或未初始化的地址,而空指针不是这种情况。 -
D. px的目标为零:指针的目标指的是它指向的对象或内存地址。空指针没有指向任何有效的目标,所以说它的“目标为零”不合适。
141. 多态是A.一个对象调用不同名称的对象 B.一个对象调用不同名称的函数 C.不同对象调用不同名称的函数 D.不同对象调用相同名称的函数
正确答案是:
D. 不同对象调用相同名称的函数
解释:
在面向对象编程中,多态(Polymorphism)的本质是不同对象可以调用相同名称的函数,但有不同的行为实现**。多态的关键在于相同的函数名可以在不同的对象或类中表现出不同的行为。
- 在运行时多态(通过虚函数实现)中,基类指针或引用可以指向不同的派生类对象,并且调用相同的函数名时,会执行派生类中的重写函数。
class Base {
public:
virtual void show() {
std::cout << "Base class" << std::endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived class" << std::endl;
}
};
int main() {
Base* ptr1 = new Base();
Base* ptr2 = new Derived();
ptr1->show(); // 输出: "Base class"
ptr2->show(); // 输出: "Derived class"
}
- 在这个例子中,相同的函数名
show()
在不同的对象(Base
和Derived
)中有不同的行为。
其他选项解释:
- A. 一个对象调用不同名称的对象:这与多态无关,多态关注的是相同函数名的不同实现。
- B. 一个对象调用不同名称的函数:这与多态无关,这是普通函数调用,与多态没有直接联系。
- C. 不同对象调用不同名称的函数:这也是普通函数调用的概念,与多态无关。多态的核心是相同名称的函数在不同对象上的不同实现。
142. C++中内存分配可以使用calloc么?
在 C++ 中,calloc
是可以使用的,但通常不推荐。C++ 提供了更现代的内存分配方式,比如 new
和 delete
,这些与 C++ 对象和构造函数的使用更为契合。
calloc
的特点:
calloc
是 C 语言中的内存分配函数,位于<cstdlib>
头文件中。- 它的原型是:
void* calloc(size_t num, size_t size);
- 它会分配一块内存并将其初始化为零,返回指向这块内存的指针。
int* arr = (int*)calloc(10, sizeof(int)); // 分配一个包含10个int元素的数组,并将其初始化为0
C++ 的 new
和 delete
:
- 在 C++ 中,建议使用
new
和delete
来分配和释放内存,因为它们不仅分配内存,还会调用对象的构造函数和析构函数,这对于对象的管理很重要。
int* arr = new int[10]; // 使用 new 分配数组,默认未初始化
如果需要分配内存并将其初始化为零,C++11 中可以使用 std::vector
或 std::fill
来初始化容器,而不必手动管理原始内存:
std::vector<int> arr(10, 0); // 创建并初始化10个元素的vector
使用 calloc
的缺点:
- 不调用构造函数:对于 C++ 对象,
calloc
只分配内存,不会调用对象的构造函数。这样就无法正确地初始化对象。 - 没有类型安全性:
calloc
返回void*
,需要显式类型转换,这不如 C++ 的new
方式类型安全。 - 现代化推荐:C++ 语言自带了更高层的工具(如
new
、std::vector
、std::unique_ptr
等),它们更适合现代 C++ 编程风格。
总结:
calloc
可以在 C++ 中使用,但一般不推荐,因为它是 C 语言的内存分配函数,而 C++ 提供了更安全和高效的内存管理方式,如new
/delete
或std::vector
。- 如果你只需要分配简单的内存块而不涉及对象,
calloc
也是可以使用的。但如果你需要分配类对象,new
或 智能指针 会是更好的选择。
143. 下列正确的是哪个?
A 未在定义语句中赋初值的auto变量和static变量的处置都是随机值
B 全局变量的作用域一定比局部变量的作用域范围大
C 静态类型变量的生存周期贯穿于整个程序的运行期间
正确的答案是:
C. 静态类型变量的生存周期贯穿于整个程序的运行期间
解释:
A. 未在定义语句中赋初值的 auto 变量和 static 变量的处置都是随机值
错误。
auto
变量:局部变量(没有显式初始化)的值是未定义的,但它不一定是随机值,取决于内存的状态。static
变量:静态变量如果未显式初始化,则会自动初始化为零(对于数值类型),而不是随机值。
因此,auto
和 static
变量的行为不同,static
变量不会是随机值。
B. 全局变量的作用域一定比局部变量的作用域范围大
错误。
- 全局变量的作用域是整个文件,除非它被声明为
static
或有其他限制,如在命名空间中。 - 局部变量的作用域是函数或块内,但是如果在内部有嵌套的作用域或局部静态变量,其生命周期在整个程序期间都有效。
所以全局变量的作用域不一定比局部变量大,作用域和生命周期是不同的概念。
C. 静态类型变量的生存周期贯穿于整个程序的运行期间
正确。
- 静态变量(包括局部静态变量和全局静态变量)在程序开始时分配内存,并在程序运行期间一直存在,直到程序结束。
- 它们的生命周期贯穿于整个程序运行时间,尽管它们的作用域可能局限于局部或全局。
144. C++默认继承方式是什么?三目运算符可以被重载么?
C++ 中的默认继承方式取决于基类的定义方式:
- 如果基类没有指定任何访问修饰符(如
public
、protected
或private
),那么默认继承方式是private
继承。 - 如果基类使用
public
关键字进行定义,则继承的方式为public
。 - 如果基类使用
protected
关键字进行定义,则继承的方式为protected
。 - 如果基类使用
private
关键字进行定义,则继承的方式为private
。
class Base {
// 默认 private 继承
};
class Derived : Base {
// 这里的继承方式是 private
};
class PublicDerived : public Base {
// 这里的继承方式是 public
};
class ProtectedDerived : protected Base {
// 这里的继承方式是 protected
};
C++ 中三目运算符(也称为条件运算符 ?:
)不能被重载。这一运算符是内置的,语法规则和运算逻辑是固定的,无法通过用户定义的方式进行重载。
145. fputc会覆盖i之前写入的内容么
fputc
函数在 C/C++ 中用于向指定的文件流写入一个字符。是否会覆盖之前写入的内容,取决于以下几个因素:
1. 文件打开模式
- 覆盖模式:如果文件是以 写模式(
"w"
)打开的,fputc
会覆盖文件中原有的内容。即在写入字符之前,文件的原内容会被清空。
FILE *file = fopen("example.txt", "w"); // 以写模式打开
fputc('A', file); // 这会写入 'A',并覆盖文件中的任何原内容
fclose(file);
- 追加模式:如果文件是以 追加模式(
"a"
)打开的,fputc
会在文件末尾添加字符,而不会覆盖任何已有内容。
FILE *file = fopen("example.txt", "a"); // 以追加模式打开
fputc('B', file); // 这会在文件末尾添加 'B'
fclose(file);
- 读写模式:如果文件是以 读写模式(如
"r+"
)打开的,fputc
会在当前文件指针位置写入字符,并可能覆盖该位置的内容。
FILE *file = fopen("example.txt", "r+"); // 以读写模式打开
fseek(file, 0, SEEK_SET); // 移动到文件开头
fputc('C', file); // 这会覆盖文件的第一个字符
fclose(file);
在使用 fputc
之前,文件指针的位置会影响写入的结果。可以使用 fseek
函数移动文件指针到所需的位置。
146. 请简述TCP/IP协议分层及理解
TCP/IP 协议分层模型是互联网通信的基础,通常分为四层,分别是:
1. 应用层(Application Layer)
- 功能:提供网络服务和应用程序的接口,处理高层协议和数据格式。应用层协议包括 HTTP、FTP、SMTP、DNS 等。
- 示例:
- HTTP:用于网页传输。
- FTP:用于文件传输。
- SMTP:用于发送电子邮件。
2. 传输层(Transport Layer)
- 功能:负责主机间的可靠或不可靠数据传输,提供数据的分段和重组,以及错误检测和流量控制。
- 主要协议:
- TCP(Transmission Control Protocol):面向连接的协议,提供可靠的、顺序的数据传输。适用于需要确保数据完整性的应用。
- UDP(User Datagram Protocol):无连接的协议,提供快速但不可靠的数据传输。适用于实时应用,如视频流和在线游戏。
3. 网络层(Internet Layer)
- 功能:负责数据包的路由和转发,确定数据从源到目的地的路径。处理不同网络之间的通信。
- 主要协议:
- IP(Internet Protocol):负责数据包的寻址和路由。主要有 IPv4 和 IPv6 两个版本。
- ICMP(Internet Control Message Protocol):用于发送错误消息和操作信息,如 ping 命令。
4. 数据链路层(Link Layer)
- 功能:负责在物理网络上实际传输数据帧,处理错误检测和修复,以及媒体访问控制。
- 示例协议:
- Ethernet:用于局域网的标准协议。
- Wi-Fi:用于无线局域网的标准协议。
综述
- 层次结构:每一层只关注相邻层之间的交互。上层协议依赖于下层提供的服务,而下层协议则不关心上层的具体实现。
- 封装与解封装:数据从应用层向下传输时,每一层都会添加自己的头部信息,形成一个封装。接收方再逐层解封装,直到数据到达应用层。
理解
TCP/IP 协议分层模型为网络通信提供了一种模块化的方式,使得不同的网络技术和设备能够互相兼容。通过将功能分层,各层可以独立发展、更新和优化,促进了网络技术的灵活性和可扩展性。
\
147. 抽象类相关的问题
class B{ public: virtual void show()=0; };
下列语句合法的是:
A. B o1
B. B *o3
C. const B *o4
D. B &o2
在 C++ 中,class B
是一个包含纯虚函数的抽象类(因为 show
是一个纯虚函数,定义为 = 0
)。由于 B
是抽象类,你不能直接创建 B
类的对象,但可以使用指针或引用来引用它。下面我们分析选项:
-
A.
B o1
- 合法性:不合法。不能直接实例化抽象类。
-
B.
B *o3
- 合法性:合法。这是一个指向
B
类型的指针,可以指向B
的派生类实例。
- 合法性:合法。这是一个指向
-
C.
const B *o4
- 合法性:合法。这是一个指向
B
类型的常量指针,可以指向B
的派生类实例,并且不允许通过该指针修改所指向的对象。
- 合法性:合法。这是一个指向
-
D.
B &o2
- 合法性:不合法。不能创建一个指向抽象类的引用,因为这意味着必须提供一个对象,但抽象类无法实例化。
148. C/S模型的邮件服务中,客户端通过Outlook收取邮件报文使用的协议有() MAIL , POP3 , SMTP , IMAP中的哪些
在 C/S(客户端/服务器)模型的邮件服务中,客户端通过 Outlook 收取邮件时,常用的协议有以下几种:
-
POP3 (Post Office Protocol version 3)
- 用途:用于从邮件服务器下载邮件到本地客户端。使用 POP3 协议后,邮件通常会从服务器上删除,因此适用于希望在本地存储邮件的用户。
- 协议特性:适合单一设备查看邮件。
-
IMAP (Internet Message Access Protocol)
- 用途:用于在邮件服务器上管理和访问邮件。IMAP 允许用户在多个设备上查看和管理邮件,因为邮件保持在服务器上。
- 协议特性:适合需要在多个设备上查看邮件的用户。
不适用的协议
-
SMTP (Simple Mail Transfer Protocol)
- 用途:用于发送邮件,而不是接收邮件。客户端使用 SMTP 协议将邮件发送到邮件服务器。
-
MAIL
- 用途:通常是一个泛指,不具体指向某种标准协议。
149. Prim算法和Kruskal算法
1.Prim算法简单描述
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
时间复杂度
这里记顶点数v,边数e
邻接矩阵:O(v2) 邻接表:O(elog2v)
2.算法简单描述
1).记Graph中有v个顶点,e个边
2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中
if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中
添加这条边到图Graphnew中
时间复杂度:elog2e e为图中的边数