每天堅持Crack Code(Day 4)

今天頭腦漲漲的,所以看書都不想從頭往後看,索性挑了一章看~
首先是瞭解一下概念問題:
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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章