每天坚持Crack Code(Day 4)

本文介绍了C++与Java的对比,探讨了C++中虚函数的工作原理,以及深拷贝和浅拷贝的区别。此外,讨论了volatile关键字的作用,并解释了C++中的名字隐藏现象和析构函数为何需要声明为虚函数。
摘要由CSDN通过智能技术生成

今天头脑涨涨的,所以看书都不想从头往后看,索性挑了一章看~
首先是了解一下概念问题:
C++ vs Java
1.Java runs in a virtual machine. 在虚拟机中运行
2. C++ natively supports unsigned arithmetic.支持原生的无符号数学运算
3. In Java, parameters are always passed by value (or, with objects, their references are passed by value). 在Java中参数总是值传递的(或者是对象的引用)
    In C++, parameters can be passed by value, pointer, or by reference.在C++中参数可以是值传递,指针和引用传递
4. Java has built-in garbage collection.内建垃圾回收
5. C++ allows operator overloading.允许运算符重载
6. C++ allows multiple inheritance of classes.多继承
Question: Which of these might be considered strengths or weaknesses of C++ or Java? Why? In what cases might you choose one language over the other?
问题:
13.1 Write a method to print the last K lines of an input file using C++.
用C++写一个函数,打印输入文件的最后k行。
方法一:暴力方式,首先读文件计算文件的行数N,然后再打开文件跳过N-k行打印最后k行。如果文件本身很大,这样一来开销很大。
方法二:我们希望只读一次文件就可以打印出最后k行。如果我们开一个大小为k的字符串数组然后循环读入,即每次%k得到第i行的字符串。

//我们希望只读一次文件就可以打印出最后k行。如果我们开一个大小为k的字符串数组然后
//循环读入,即每次%k得到第i行的字符串。
void printLastKLines(ifstream &fin, int k)
{
	string L[k];
	int lines = 0;
	string tmp;
	while(getline(fin, tmp))
	{
		L[lines%k] = tmp;
		++lines;
	}
	//if less than K lines were read,print them all
	int start, count;
	if(lines
  
  

13.2 Compare and contrast a hash table vs. an STL map. How is a hash table implemented? If the number of inputs is small, what data structure options can be used instead of a hash table?
翻译:hash表与STL map对比,hash表如何实现的?如果输入规模不大,我们应该用什么数据结构代替hash表?
STL map是通过红黑树实现的,增删改查找都是O(logn)的时间复杂度,不需要处理冲突问题。hash表示以空间换取时间,通过hash函数提供key到value,需要解决冲突问题,时间复杂度O(1)。如果输入数据规模不大就可以用STL map来代替hash表。
hash表如何实现:
1.首先要一个好的hash函数来确保hash值时均匀分布的,比如:对大质数取模;
2.其次是需要一个好的冲突解决方法,表中元素密集时用(chaining链式法),表中元素稀疏时用(probing探测法);
3.动态地增加或减少hash表的大小。比如,(表中元素数量)/(表的大小)大于一个阈值时,就增加hash表的大小,新建一个大的hash表,然后根据hash函数将旧表映射到新表中。
13.3 How do virtual functions work in C++?
翻译:C++中虚函数是如何工作的?
虚函数依赖虚函数表进行工作。如果一个类中,有函数被关键词virtual进行修饰, 那么一个虚函数表就会被构建起来保存这个类中虚函数的地址。同时, 编译器会为这个类添加一个隐藏指针指向虚函数表。如果在派生类中没有重写虚函数, 那么,派生类中虚表存储的是父类虚函数的地址。每当虚函数被调用时, 虚表会决定具体去调用哪个函数。因此,C++中的动态绑定是通过虚函数表机制进行的。当我们用基类指针指向派生类时,虚表指针vptr指向派生类的虚函数表。 这个机制可以保证派生类中的虚函数被调用到。C++中非虚函数的调用是在编译阶段静态绑定的,而虚函数的调用则是在执行阶段动态绑定的。
13.4 What is the difference between deep copy and shallow copy? Explain how you would use each.
翻译:深拷贝和浅拷贝的区别是什么?如何使用它们?
浅拷贝并不是复制数据,只复制指向数据的指针,因而只是多了一个指针指向同一份数据;深度拷贝回复制原始数据,每个指针指向一份独立的数据。
Declaring a pointer variable for a volatile memory (only the pointer address is volatile):
volatile int * x;
int volatile * x;
Declaring a volatile pointer variable for a non-volatile memory (only memory contained is volatile):
int * volatile x;
Declaring a volatile variable pointer for a volatile memory (both pointer address and memory contained are volatile):
volatile int * volatile x;
int volatile * volatile x;
Volatile variables are not optimized, but this can actually be useful. Imagine this function:
1 int opt = 1;
2 void Fn(void) {
3 start:
4 if (opt == 1) goto start;
5 else break;
6 }
At first glance, our code appears to loop infinitely. The compiler will try to optimize it to:
1 void Fn(void) {
2 start:
3 int opt = 1;
4 if (true)
5 goto start;
6 }
浅拷贝在构造和删除对象的时候容易产生问题,特别是析构函数,当多个指针指向同一份内存时,删除这些指针会多次释放内存空间而出错。智能指针可以改善浅拷贝,用一个引用计数来记录指向同一块内存的指针数量,只有当指针数量为0时才释放内存空间。
13.5 What is the significance of the keyword “volatile” in C?
翻译:C语言中的关键字”volatile(易变的)“的意义。
因为访问寄存器比访问内存要快得多,所以编译器一般都会做减少存取内存的优化,volatile这个关键字会提醒编译器,它声明的变量随被外部修改,因此不要进行编译优化,以免出错。
Declaring a simple volatile variable:
volatile int x;
int volatile x;
如果我们给opt加上volatile,表明外部会修改opt值,编译器就不会做上述优化。
13.6 What is name hiding in C++?
翻译:C++中名字隐藏是什么?
让我们通过一个例子来讲解C++中的名字隐藏。在C++中,如果一个类里有一个重载的方法, 你用另一个类去继承它并重写(覆盖)那个方法。你必须重写所有的重载方法, 否则未被重写的方法会因为名字相同而被隐藏,从而使它在派生类中不可见。
For example:
1 class FirstClass {
2 public:
3 virtual void MethodA (int);
4 virtual void MethodA (int, int);
5 };
6 void FirstClass::MethodA (int i) {
7 std::cout << “ONE!!\n”;
8 }
9 void FirstClass::MethodA (int i, int j) {
10 std::cout << “TWO!!\n”;
11 }
This is a simple class with two methods (or one overloaded method). If you want to override the one-parameter version, you can do the following:
1 class SecondClass : public FirstClass {
2 public:
3 void MethodA (int);
4 };
5 void SecondClass::MethodA (int i) {
6 std::cout << “THREE!!\n”;
7 }
8 void main () {
9 SecondClass a;
10 a.MethodA (1);
11 a.MethodA (1, 1);
12}

上面main函数中,第二个MethodA在编译时会报错,因为它在派生类中是不可见的,这就是名字隐藏。名字隐藏与虚函数无关,所以不论基类中那两个函数是不是虚函数,都会名字隐藏。解决方法有:

1.将第二个MethodA换一个名字,但是这里是重载函数,因此换名字不是好办法;

2.在派生类中重写所有的重载函数。

13.7 Why does a destructor in base class need to be declared virtual?

翻译:为何基类中的析构函数要声明为虚函数?

用对象指针来调用一个函数,有以下两种情况:

  1. 如果是虚函数,会调用派生类的版本。

  2. 如果是非虚函数,会调用指针所指类型的实现版本。

析构函数也会遵循以上两种情况, 当对象出了作用域或是我们删除对象指针,析构函数就会被调用。当派生类对象出了作用域,派生类的析构函数会先调用,然后再调用它父类的析构函数, 这样能保证分配给对象的内存得到正确释放。但是,如果我们删除一个指向派生类对象的基类指针,而基类析构函数又是非虚的话, 那么就会先调用基类的析构函数,派生类的析构函数得不到调用。

For example:
1 class Base {
2 public:
3 Base() { cout << “Base Constructor “ << endl; }
4 ~Base() { cout << “Base Destructor “ << endl; } /* see below */
5 };
6 class Derived: public Base {
7 public:
8 Derived() { cout << ”Derived Constructor “ << endl; }
9 ~Derived() { cout << ”Derived Destructor “ << endl; }
10 };
11 void main() {
12 Base *p = new Derived();
13 delete p;
14 }
Output:
Base Constructor
Derived Constructor
Base Destructor

If we replace the above destructor with:
1 virtual ~Base() {
2 cout << “Base Destructor” << endl;
3 }

Then the output becomes:
Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

因此,只要一个类被其他类继承,就应该声明析构函数为虚函数。

13.8 Write a method that takes a pointer to a Node structure as a parameter and returns a complete copy of the passed-in data structure. The Node structure contains two pointers to other Node structures.

13.9 Write a smart pointer (smart_ptr) class.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值