一个C++关于打印log的面试题

基础代码如下,题目都是根据这个代码翻新出来的。

#include<iostream>
#include<stdlib.h>
#include<fstream>
using namespace std;

class Log{
public:
	Log(int id, const std::string& filename):m_id(id){
		cout<<"create"<<id<<endl;
	}

	~Log(){
		cout<<"bye bye"<<endl;
	}

	void log(const std::string& info){}

private:
	int m_id;
};

void test(){
	static Log log(1,"log.log");
	log.log("hello");
}

void test2(){
	static Log log(2,"log.log");
	log.log("world");
}

int main(){
	test();
	test2();
	return 0;
}

运行结果:

可见,test和test2的调用顺序是——先调用test,然后调用test2的构造函数,先回收test2中的资源,然后回收test中的资源

在调用test函数时:

先构造了一个log类,然后输出了一个log日志。

注意这里test和test2的filename都是log.log,说明两个函数打开了相同的文件log.log,问题就出在这里。那么输出的结果会是什么样的?有几个备选答案:

  1. hello
  2. world
  3. hello world
  4. world hello

这四种都可能会出现出来。

上面的代码存在两个问题,一个是出在Log类里面的log函数里,一个是出现在本身的设计上。将上述代码稍作修改如下:

#include<iostream>
#include<stdlib.h>
#include<fstream>
using namespace std;

class Log{
public:
	Log( const std::string& filename){	
		//打开文件
		m_f.open(filename.c_str(), fstream::out);
	}

	~Log(){	}

	void log(const std::string& info){
		cout<<info;
		m_f<<info;
	}

private:
	fstream m_f;
};

void test(){
	static Log log("log.log");
	log.log("hello");
}

void test2(){
	static Log log("log.log");
	log.log("world");
}

int main(){
	test();
	test2();
	return 0;
}

上面的代码中,执行发stream时,会打开一个文件,因此上面的代码在执行test函数时,会打开文件名为log.log的文件,打开文件之后,我们会获得该文件的文件描述符(类似于一个指向该文件的指针),之后输入“hello”; 接下来test2打开相同的文件,输入“world”。此时的运行结果为:

成功的按顺序打印了helloworld,那么我们打开log.log文件看看:

里面只有一个hello,为什么?

这与操作系统有关系。因为fstream是基于缓存的,执行fstream时,会打开一个文件,因此上面的代码在执行test函数时,会打开文件名为log.log的文件,打开文件之后,我们会获得该文件的文件描述符,之后将“hello”写入到缓存中

接下来test2打开相同的文件,将“world”写入到缓存中

当程序执行完毕时,会调用析构函数来回收资源,先回收test2的资源,此时发现缓存中存在test2要写入文件的“world”,因此将“world”写入到文件中

然后调用test的析构函数,此时发现test要将“hello”写入到文件中,因此将“hello”写入到文件中由于两个文件描述符指向了相同的文件,因此“world”就把“hello”覆盖掉了。

既然如此,那我们在log函数里面加一个flush函数,将缓存中的数据刷到文件中不就解决问题了吗?修改后的log函数如下:

再执行一次程序:

成功输出了helloworld,那么我们打开文件log.log查看一下:

文件里面只有一个world,为什么?

还是因为两个文件描述符打开的是同一个文件的缘故,因此后来者“world”覆盖掉了之前写在文件里的“hello”。

既然如此,我们将log对象作为单例放在函数外,让进程只拥有一个文件描述符,就可以正确的把helloworld输入在文件中了:

#include<iostream>
#include<stdlib.h>
#include<fstream>
using namespace std;

class Log{
public:
	Log( const std::string& filename){	
		//打开文件
		m_f.open(filename.c_str(), fstream::out);
	}

	~Log(){	}

	void log(const std::string& info){
		cout<<info;
		m_f<<info;
		m_f.flush();
	}

private:
	fstream m_f;
};

static Log log("log.log");

void test(){
	log.log("hello");
}

void test2(){
	log.log("world");
}

int main(){
	test();
	test2();
	return 0;
}

此时在屏幕上和在文件里的输出就都正确了:

补充:

1、因为Log类的对象log是static的,因此,函数test结束并不会导致该对象被析构。static对象只有在程序运行结束是才会被析构。

2、由于flush函数会对性能造成一定的影响,因此一般情况下不调用该函数,但是也因此会有可能造成数据的丢失

(下面代码,亲测输出“helloworld”,没有用flush函数)

#include<iostream>
#include<stdlib.h>
#include<fstream>
using namespace std;

class Log{
public:
	Log( const std::string& filename){	
		//打开文件
		m_f.open(filename.c_str(), fstream::out);
	}

	~Log(){	}

	void log(const std::string& info){
		cout<<info;
		m_f<<info;
		//m_f.flush();
	}

private:
	fstream m_f;
};

static Log log("log.log");

void test(){
	log.log("hello");
}

void test2(){
	log.log("world");
}

int main(){
	test();
	test2();
	return 0;
}

那么,如何输出“worldhello”呢?

猜你喜欢

转载自blog.csdn.net/qq_29996285/article/details/87135958
今日推荐