C++ 面向对象高级编程 (下) week 1 (Boolan)

本课程是上一门视频课程"面向对象程序设计"(即: C++面向对象高级编程(上))的续集, 将继续探讨一些未及讨论的主题:

  • operator type() const
  • explicit complex(...): initialization list { }
  • pointer-like object
  • function-like object
  • namespace
  • template specialization
  • Standard library
  • variadic template (C++ 11)
  • auto (C++ 11)
  • range-base for loop (C++ 11)

课程目标

  • 在先前基础课程所培养的正规, 大器的编程素养上, 继续探讨更多技术.
  • 泛型编程(Generic Programming)和面向对象编程(Object-Oriented Programming)虽然分属不同的思维, 但它们正是C++的技术主线, 所以本课程也讨论template(模板).
  • 深入探索面向对象之继承关系(inheritance)所形成的对象模型(Object Model), 包括隐藏于底层的this指针, vptr(虚指针), vtbl(虚表), virutual mechanism(虚机制), 以及虚函数(virtual functions)造成的polymorphism(多态)效果.

推荐书目

  1. C++ Primer (5th Edition) by Stanley B. Lippman
  2. The C++ Programming Language (4th Edition) by Bjarne Stroustrup
  3. Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) by Scott Meyers
  4. Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14 by Scott Meyers
  5. The C++ Standard Library - A Tutorial and Reference (2nd Edition) by Nicolai M. Josuttis
  6. STL源码剖析 by 侯捷

点评: 选择一本好的C++书籍(1和2两本书皆可), 然后以及尽量多的完成书籍的习题,可以帮助完成C++语法的学习; 熟悉C++的语法后,通过3和4学习如何正确高效地使用C++/C++11/C++14; 如果对STL的一些实现有兴趣,可参考书目5和6.

conversion operator, 转换函数

class Fraction {
 public:
  Fraction(int num, int den = 1) 
   : m_numerator(num), m_denominator(den) { }
  operator double() const  {
    return (double) m_numerator / m_denominator;
  }
 private:
  int m_numerator;    // 分子
  int m_denominator;  // 分母
};
Fraction f(3, 5);
double d = 4 + f;  // 调用operator double()将f转为0.6

摘自C++ Primer:

A conversion operator is a special kind of member function that converts a value of a class type to a value of some other type. A conversion function typically has the general form
operator type() const;
where type represents a type. Conversion operators can be defined for any type (other than void) that can be a function return type. Conversions to an array or a function type are not permitted. Conversions to pointer types—both data and function pointers—and to reference types are allowed.
Conversion operators have no explicitly stated return type and no parameters, and they must be defined as member functions. Conversion operations ordinarily should not change the object they are converting. As a result, conversion operators usually should be defined as const members.

non-explicite-one-argument ctor

class Fraction {
 public:
  Fraction(int num, int den = 1)
   : m_numerator(num), m_denominator(den) { }
  Fraction operator+(const Fraction &f) {
    return Fraction(......);
  }
 private:
  int m_numerator;
  int m_denominator; 
};
Fraction f(3, 5);
Fraction d2 = f + 4;  // 调用non-explicit ctor将4转为Fraction(4,1)
                      // 然后调用operator+

Note (摘自C++ Primer):

A constructor that can be called with a single argument defines an implicit conversion from the constructor’s parameter type to the class type.

Why explicit?

class Fraction {
 public:
  Fraction(int num, int den = 1)
   : m_numerator(num), m_denominator(den) { }
  operator double() const  {
    return (double) m_numerator / m_denominator;
  }
  Fraction operator+(const Fraction &f) {
    return Fraction(......);
  }
 private:
  int m_numerator;
  int m_denominator; 
};
Fraction f(3, 5);
Fraction d2 = f + 4;  // [Error] ambiguous

点评: 在这个例子中, conversion function负责将Fraction转化为double, 而non-explicit-one-argument ctor负责将double转化为Fraction. 然而当conversion function和non-explicit-one-argument ctor同时存在时, 程序出现了二义性问题. 对于编译器而言, Fraction d2 = f + 4可以理解成4转为Fraction然后调用operator+,同时也可以理解成f转成double, 和4做加法后再转为Fraction, 所以程序是无法编译通过的.

新的explicit-one-argument ctor:

  explicit Fraction(int num, int den = 1)
   : m_numerator(num), m_denominator(den) { }

关于explicit(摘自C++ Primer):

We can prevent the use of a constructor in a context that requires an implicit conversion by declaring the constructor as explicit. The explicit keyword is meaningful only on constructors that can be called with a single argument. Constructors that require more arguments are not used to perform an implicit conversion, so there is no need to designate such constructors as explicit. The explicit keyword is used only on the constructor declaration inside the class. It is not repeated on a definition made outside the class body.

pointer-like classes, 关于智能指针

template <class T>
class shared_ptr {
 public:
  T& operator*() const { return *px; }
  T* operator->() const { return px; }
  shared_ptr(T* p): px(p) { }
 private:
  T *px;
  long *pn;
};

关于pointer-like class和value-like class(摘自C++ Primer):

Classes that behave like values have their own state. When we copy a valuelike object, the copy and the original are independent of each other. Changes made to the copy have no effect on the original, and vice versa.
Classes that act like pointers share state. When we copy objects of such classes, the copy and the original use the same underlying data. Changes made to the copy also change the original, and vice versa.
Of the library classes we’ve used, the library containers and string class have valuelike behavior. Not surprisingly, the shared_ptr class provides pointerlike behavior, as does our StrBlob class (§ 12.1.1, p. 456). The IO types and unique_ptr do not allow copying or assignment, so they provide neither valuelike nor pointerlike behavior.

function-like classes, 所谓仿函数

template <class T>
struct identity {
  const T& operator() (const T &x) const { return x; }
};

template <class Pair>
struct select1st {
  const typename Pair::first_type&
  operator() (const Pair &x) const
  { return x.first; }
};

template <class Pair>
struct select2st {
  const typename Pair::second_type&
  operator() (const Pair &x) const
  { return x.second; }
};

Function-like class/functor (摘自Numberical Recipes):

A functor is simply an object in which the operator () has been overloaded to play the role of returning a function value.

namespace

关于namespace的定义(摘自C++ Primer):

A namespace definition begins with the keyword namespace followed by the namespace name. Following the namespace name is a sequence of declarations and definitions delimited by curly braces. Any declaration that can appear at global scope can be put into a namespace: classes, variables (with their initializations), functions (with their definitions), templates, and other namespaces. As with any name, a namespace name must be unique within the scope in which the namespace is defined. Namespaces may be defined at global scope or inside another namespace. They may not be defined inside a function or a class.

specialization, 模板特化

template <class Key>
struct hash { ...... };
template <>
struct hash<char> {
  size_t operator() (char x) const { return x; }
};

template <>
struct hash<int> {
  size_t operator() (int x) const { return x; }
}; 

template <>
struct hash<long> {
  size_t operator() (long x) const { return x; }
};

关于模板特化(摘自C++ Primer):

Redefinition of a class template, a member of a class template, or a function template, in which some (or all) of the template parameters are specified. A template specialization may not appear until after the base template that it specializes has been declared. A template specialization must appear before any use of the template with the specialized arguments. Each template parameter in a function template must be completely specialized.

partial specialization, 模板偏特化

  • 个数的偏:
// original class template:
template <typename T, typename Alloc = ......>
class vector {
...
};

// partial specialization:
template <typename Alloc = ......>
class vector <bool, Alloc> {
...
};
  • 范围的偏:
// original class template:
template <typename T>
class C
{
// ...
};

// partial specialization:
template <typename T>
class C<T*>
{
// ...
};

关于模板偏特化(摘自C++ Primer):

Differently from function templates, a class template specialization does not have to supply an argument for every template parameter. We can specify some, but not all, of the template parameters or some, but not all, aspects of the parameters. A class template partial specialization is itself a template. Users must supply arguments for those template parameters that are not fixed by the specialization.

关于C++标准库

点评: 标准库是C++重要的组成部分, 包括了容器, 迭代器, 算法和仿函数. 这是一个极其巨大的话题, 三言两语是无法解释的. 完成了C++基础语法的学习后, 应好好参考侯捷老师的"那本STL源码剖析".

variadic templates (since C++ 11)

void print()
{
}

template <typename T, typename... Types>
void print(const T &firstArg, const Types&... args) {
  cout << firstArg <<endl;
  print(args...);
}

print(7.5, "hello", bitset<16>(377), 42);
// OUTPUT:
// 7.5
// hello
// 0000000101111001
// 42

关于variadic templates(摘自 C++ Primer):

A variadic template is a template function or class that can take a varying number of parameters. The varying parameters are known as a parameter pack. There are two kinds of parameter packs: A template parameter pack represents zero or more template parameters, and a function parameter pack represents zero or more function parameters.

auto (since C++11)

list<string> c;
...

// before C++11
list<string>::iterator ite = find(c.begin(), c.end(), target);

// since C++11
auto                  ite2 = find(c.begin(). c.end(), target);

关于auto(摘自C++ Primer):

Under the new standard, we can let the compiler figure out the type for us by using the auto type specifier. Unlike type specifiers, such as double, that name a specific type, auto tells the compiler to deduce the type from the initializer. By implication, a variable that uses auto as its type specifier must have an initializer.

range-based for (since C++11)

for (int i : {2, 3, 5, 7, 9, 13, 17, 19}) {
  cout << i << endl;
}

vector<double> vec;
...
// pass by value
for (auto elem : vec) {
  cout << elem << endl;
}

// pass by reference
for (auto &elem : vec) {
  elem*=3;
}

关于range-based for(摘自C++ Primer):

If we want to do something to every character in a string, by far the best approach is to use a statement introduced by the new standard: the range for statement. This statement iterates through the elements in a given sequence and performs some operation on each value in that sequence. The syntactic form is
for (declaration : expression) { statement }
where expression is an object of a type that represents a sequence, and declaration defines the variable that we’ll use to access the underlying elements in the sequence. On each iteration, the variable in declaration is initialized from the value of the next element in expression .

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容