天天看點

第十六章 16.1.1節練習

練習16.1

給出執行個體化的定義。

解答:

執行個體化,就是編譯器将一個函數模闆中的類型用一個具體類型替換的過程。

【引用百度百科】在面向對象的程式設計中,通常把用類建立對象的過程稱為執行個體化。

練習16.2

編寫并測試你自己版本的compare函數。

解答:

這個函數模闆的實作,可以參考書中的實作。

不過這裡要注意的是,當你的實作中涉及到比較運算符的時候,需要确定你使用的類型支援比較運算符。

練習16.3

對兩個Sales_data對象調用你的compare函數,觀察編譯器在執行個體化過程中如何處理錯誤。

解答:

這個其實就是使用沒有比較運算符的類進行比較。

#include <functional>
#include <iostream>

class A{
  public:
    A() = default;
    A(const int& n):num(n){}
    int num;
};

template<typename T>
int compare(const T& a, const T& b){
  if(std::less<T>()(a, b)){
    return 1;
  } else if(std::less<T>()(b, a)){
    return -1;
  } else {
    return 0;
  }
}

int main(){
  int ia(10), ib(12);
  std::cout << compare(ia, ib) << std::endl;
  A a(10), b(12);
  std::cout << compare(a, b) << std::endl;
}
           

這裡就沒有使用Sales_data類。自行定義了一個沒有比較操作符的類。

然後,可以看一下編譯器那邊會給出什麼提示。

我這邊用的g++4.9.1出現的錯誤資訊比較長,不過意思都差不多,就是沒有找到‘<’這個比較運算符。

練習16.4

編寫行為類似标準庫find算法的模闆。函數需要兩個模闆類型參數,一個表示函數的疊代器參數,另一個表示值的類型。使用你的函數再一個vector<int>和一個list<string>中查找給定值。

解答:

#include <iostream>
#include <iterator>
#include <vector>
#include <list>

template<typename Iterator, typename T>
Iterator find(const Iterator& begin, const Iterator& end, const T &element){
  for ( Iterator it = begin; it != end; ++it){
    if (*it == element){
      return it;
    }
  }
  return end;
}

int main(){
  int num[] = {1,2,3,4,5,6,7,8,9,10};
  std::vector<int> vint(std::begin(num), std::end(num));
  std::list<int> lint(std::begin(num), std::end(num));

  auto iit = find(vint.cbegin(), vint.cend(), 2);
  std::cout << *iit << std::endl;


  auto lit = find(lint.cbegin(), lint.cend(), 3);
  std::cout << *lit << std::endl;
}
           

練習16.5

為6.2.4節(第195頁)中的print函數編寫模闆版本,它接受一個數組和引用,能處理任意大小、任意元素類型的數組。

解答:

#include <iostream>
#include <string>

template <typename T, std::size_t Num>
void print(T (&arr)[Num]){
  for (auto elem : arr){
    std::cout << elem << std::endl;
  }
}

int main(){
  int num[] = {1,2,3};
  std::string str[] = {"one", "two", "three"};

  print(num);
  print(str);
}
           

練習16.6

你認為接受一個數組實參的标準庫函數begin和end是如何工作的?定義你自己版本的begin和end。

解答:

#include <iostream>

template <typename T, std::size_t Num>
T* begin(T (&Container)[Num]){
  return Container;
}

template <typename T, std::size_t Num>
T* end(T (&Container)[Num]){
  return (Container + Num);
}

int main(){
  int num[] = {1,2,3,4,5,6};
  std::cout << begin(num) <<": val is " << *begin(num) << std::endl;
  std::cout << end(num) - 1 <<": val is " << *(end(num) - 1) << std::endl;
  std::cout << end(num) << std::endl; //将列印最後一個元素後面的的位址
}
           

練習16.7

編寫一個constexpr模闆,傳回給定數組的大小。

解答:

#include <iostream>

template <typename T, std::size_t Num>
size_t cconstexpr(T (&arr)[Num]){
  return Num;
}

int main(){
  int num[] = {1,2,3,4,5,6,7,8};
  std::cout << cconstexpr(num) << std::endl;
}
           

在c++11标準中constexpr已經是關鍵字了,是以不能使用其作為函數的名字,是以這裡多加了一個c。

不過,在編譯的時候将-std=c++11這個選項去掉,使用constexpr作為函數名就沒有問題。

在VS2013中,應該也有對應C++11的選項,不過現在的環境沒有VS2013,是以這個可能要晚些時候補上了。

練習16.8

再第97頁的“關鍵概念”中,我們注意到,C++程式員喜歡用!=而不喜歡<。解釋這個習慣的原因。

解答:

這裡就是差別C和C++的地方,C++主打面向對象程式設計,而C是面向過程程式設計。

C++中程式設計的核心逐漸向不同類型的對象進行轉移,這些對象不一定支援小于的操作,而不等于的操作更為友善使用。

同樣,使用不等于也可以減少很多不必要的實作,進而能讓泛型程式設計的代碼更加優美。

C的話,大多數情況下都是對内置類型進行操作,是以在小于和不等于的選擇中,肯定選擇比較好了解的那個。

當然這裡還是有曆史原因的,教課書編寫者的一些程式設計習慣,也會潛移默化的影響讀者。

繼續閱讀