C/C++ 答疑解问

1. sizeof(string)的大小

string属于类,类的大小就是类中成员变量(非静态)加上指向虚函数表的指针以及指向虚基类表的指针加起来的和。因为string是一个模板类,受具体的实现来决定其sizeof。实际上,到了C++中,对类的sizeof往往没有意义。这涉及到编译器在实现类对象时采用的数据结构。

VC6.0:sizeof(string) = 16;
VS2010:sizeof(string) = 32;

可以发现std::string在VC6.0和VS2010里面的实现并不相同。


 2. new、delete与malloc、free的关系

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

参考:malloc/free和new/delete的区别


 3. 泛化、关联、组合、聚合以及依赖的区别

依赖:用到了别人的方法或变量;关联:对称的,好比你是我的朋友,我也是你的朋友;聚合:非对称的,员工与公司就是聚合关系,还有一个重要特点就是生命周期可以不同,员工离开了公司还是可以活的;组合:生命周期一致,好比人与心脏,一个没了另一个也没了。强度:依赖<关联<聚合<组合。

详见:关联、组合、聚合、依赖关系比较


 4. 分别写出BOOL,int,float,指针类型的变量与“零”的比较语句。

BOOL: if (flag) or if (!flag)
int:    if (n == 0) or if(n != 0)
float:   const float EPSINON = 0.00001;
     if ((x >= - EPSINON) && (x <= EPSINON))
     其中EPSINON 是允许的误差(即精度),在允许误差内的就认为是相等的
pointer: if (p == NULL) or if(p != NULL)

参见:零值比较--BOOL,int,float,指针变量与零值比较的if语句


5. (1)如何打印出当前源文件的文件名以及源文件的当前行号?

cout << __FILE__;
cout << __LINE__;

__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

  (2)如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

#ifdef __cplusplus//两个下划线
cout<<"c++";
#else
cout<<"c";
#endif

6. 在C++程序中调用被C编译器编译后的函数,为什么要加extern “C”?  

详见:C++中extern “C”含义深层探索


7. 写出Windows消息机制的流程。

(1)操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。
(2)应用程序在消息循环中调用GetMessage函数从消息队列中取出一条条的消息。取出后,以对消息进行一些预处理,如放弃对某些消息的响应,或者调用TranslateMessage产生新的消息。
(3)应用程序调用DispatchMessage,将消息回传给操作系统。
(4)系统利用窗口过程函数调用窗口过程,对消息进行处理。

通常我们编写的消息循环代码如下:

while(GetMessage (&msg, NULL, 0, 0))
{
    TranslateMessage (&msg) ;
    DispatchMessage (&msg) ;
}

TranslateMessage函数将虚拟键消息转换为字符消息。DispatchMessage实际上是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理。
从消息队列中获取消息还可以调用PeekMessage函数,发送消息可以使用SendMessage和PostMessage函数。


8. 引用与指针有什么区别?

(1)引用必须被初始化,指针不必。
(2)引用初始化以后不能被改变,指针可以改变所指的对象。
(3)不存在指向空值的引用,但是存在指向空值的指针。
(4)从内存上来讲,系统为指针分配内存空间,而引用与绑定的对象共享内存空间,系统不为引用变量分配内存空间。所以引用访问对象是直接访问,指针访问对象是间接访问。


9. 网络相关知识

集线器Hub工作在OSI参考模型的(物理)层;交换机Switch工作在OSI参考模型的(数据链路)层;路由器Router工作在OSI参考模型的(网络)层;

IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与之后才能区分哪些是网络位哪些是主机位。

ARP(Address Resolution Protocol)地址解析协议将IP地址转化为主机物理地址(MAC地址),对应RARP协议。

ICMP:Internet Control Message Protocol(网际控制报文协议)的缩写。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP的一个重要应用就是分组网间探测PING(Packet InterNet Groper),用来测试两个主机之间的连通性。
另一个非常有用的应用是traceroute(UNIX系统中名字,Windows对应的命令是tracert),它用来跟踪一个分组从源点到终点的路径。

DHCP:(Dynamic Host Configuration Protocol)动态主机配置协议,是一种让系统得以连接到网络上,并获取所需要的配置参数手段。这种机制允许一台计算机加入新的网络和获取IP地址而不用手工参与。


 10. (1) 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)。

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

在C语言中,宏是产生内嵌代码的唯一方法。对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法。
注意几点:
1). #define 语法的基本知识(例如:不能以分号结束,最好将宏定义中的“参数”和整个宏用用括号括起来),函数宏被调用时只是进行简单的字符替换,而不是“值传递”。
2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
4). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。

(2) 写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

#define MIN(A,B) ((A)<=(B) ? (A):(B))

而比如代码:least = MIN(*p++, b); 将被替换为:((*p++)<=(b) ? (*p++):(b)),发生的事情无法预料,因而不要给宏定义传入有副作用的"参数"。


11. const 和 #define的区别

(1) 编译器处理方式不同:define宏是在预处理阶段展开;const常量是编译运行阶段使用。
(2) 类型和安全检查不同:define宏没有类型,不做任何类型检查,仅仅是字符替换,并且在字符替换可能会产生意料不到的错误(边际效应);const常量有具体的类型,在编译阶段会执行类型检查。有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
(3) 存储方式不同:define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存;const常量会在内存中分配(可以是堆中也可以是栈中)

在C++ 程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。


12. 设计模式:工厂模式和单例模式介绍一下?

单体模式 很简单,就是将构造函数变为私有,那么就不能通过new来创建对象。同时创建一个共有的静态的方法来获得实例,代码如下:C++

//Singleton.h
class Singleton  
{
public:
	static Singleton* GetInstance();
private:
	Singleton() {}
	static Singleton *singleton;
};
//Singleton.cpp
Singleton* Singleton::singleton = NULL;
Singleton* Singleton::GetInstance()
{
	if(singleton == NULL)
		singleton = new Singleton();
	return singleton;
}

工厂模式有三个参与者,抽象产品(Product)、工厂(Creator)和具体产品(ConcreteProduct)。客户只会看到工厂和抽象产品。

public interface Product{ 
    public String getName(); 
} 

public class ConcreteProduct implements Product{ 
    public String getName(){ 
        return "产品1"; 
    } 
} 

public class Creator{ 
    public static Product create1(){ 
        return new ConcreteProduct(); 
    } 
}

工厂模式的作用在于将创建具体产品的方法由工厂类控制,客户只需要知道产品的抽象类型。比如具体产品可以是:香蕉、苹果、橘子等,只需要通知一声工厂,工厂就会调用相应的 生产香蕉、生产苹果、生产橘子等函数,而客户不需要知道生产这些水果的具体过程,坐着等吃就行。

原文地址:https://www.cnblogs.com/li-chong/p/3261527.html