《thinking in c++》Volume 1知识点总结(二)

Chapter9: Inline Function

1、C++中我们不会使用macro定义成员函数,因为macro没有权限访问数据成员;macro不属于任何类;对于类对象不能使用“.”运算符;且没有被封装,一但被定义就可以被任何类使用

相反,C++inline function可以解决这个问题

2、Inline function

inline function确实是函数,且在调用时被扩展

class里定义的函数默认为inline function,在class外定义的函数应在前面加上inline关键字,才为inline function

inline function常用于accessorsmutators

3、不能实现函数inline的情况

函数太复杂,具有循环或代码很长

函数的地址被引用

Recursive reference

class X{

        int i;

public:

       int f()const{ return g()+1; } //forward referenceinlining is ok

       int g()const{ return i; }

};

class X{

       int i;

public:

       int f()const{ return g()+1; } //recursive referenceinlining is impossible

       int g()const{ return f()+i; }

};

4、Hidden activities in constructors& destructors

class Member {

       int i, j, k;

public:

     Member(int x = 0) : i(x), j(x), k(x) {  }

     ~Member() { cout << "~Member" << endl; }

};

class WithMembers {

     Member q, r, s; 

     int i;

public:

     WithMembers(int ii) : i(ii) { }

    ~WithMembers() { cout << "~WithMembers" << endl; }

};

此时构造WithMembers还需调用3Member的构造函数,因此将WithMembersconstructor定义为inline反而降低了效率

 

Chapter10: Name Control

1、Static objects inside functions

函数第一次被调用时,static objects才调用constructor进行构造,且只构造一次

当程序调用exit()退出时,才调用destructor析构static objects

如果函数没有被调用,则static objects不会被构造更不会析构

构造顺序:global>static 析构顺序:static>global

程序的对象析构顺序总是与构造顺序相反的

2、Namespace:在一个大项目中避免名字冲突的有效方法

可以内嵌,且结尾处没有分号

一个名字空间的定义可以出现在几个头文件中,这不会造成重定义

可以运用别名:namespace Bob=BobsSuperDuperLibrary;

3、Unnamed namespacestatic具有相同的作用

namespace {

class Arm  { /* ... */ };

      class Robot {

            Arm arm[4];

            ...

      } xanthan;

     int i, j, k;

}

int main() { }

4、当声明某个函数f()为某一namespace中某一classfriend函数时,该函数f()会变为该名字空间的一员

5、Declare a namespace member:以cout为例

Scope resolution

std::cout<<num<<num;

The using directive

using namespace std;

cout<<num<<num;

The using declaration

using std::cout;

cout<<num<<num;

6、当两个名字空间中具有相同名函数时,作用域内declare范围小的会被调用(范围scope resolution>the using directive>the using declaration),且与顺序无关

若同时出现两个declare范围相同,则会造成冲突出错

namespace U {

   inline void f() { }

}

namespace V {

   inline void f() { }

void h() {

using V::f; 

   using namespace U; 

   f();   // V::f() will be called.

   U::f(); 

}

void g() {

using namespace V;

   using namespace U; 

   !f();   //wrong!

   U::f(); 

}

7、Using namespace

在头文件不要使用global using directive,在cpp文件使用global using directive

使用explicit qualification or declaration避免名字冲突

8、Static data membersa single piece of storage shared by all objects of a class

在类内进行声明,必须在类外部进行有且仅有一次的定义,可以更改

int x = 100;

class WithStatic {

   static int x;

   static int y;

};

int WithStatic::x = 1;

int WithStatic::y = x + 1; //x is WithStatic’s x

9、Static member functionsEssentially a global function which is scoped inside a class

static member function可以不通过类对象直接调用,即X::fun()

函数没有this指针,只能访问static data members

函数中不能调用non-static member function

10、Make a singleton:只有一个对象的类

class Egg {

int i;

    static Egg singleton; //must be static

    Egg(int ii) { i=ii; } //avoid calling constructor

    Egg(const Egg &); //avoid copy-constructor

public:

    static Egg * instance( ) {return & singleton; } //must be static to access singleton

    void print()  { cout << i << endl; }

};

Egg Egg::singleton(0); //define the only object

int main() {

    Egg::instance( )->print( ); 

}

11、Static initialization dependency

// is added to the project file first

#include <fstream>

extern std::ofstream  out;

class X{

public:

        X() { out << "hello" << std::endl;} //wrong, out hasn’t been defined!

} x;

 

// is added to the project file later

#include <fstream>

std::ofstream out("data.txt");

 

Solution1Use an additional class

#ifndef INIT_H

#define INT_H

#include <fstream>

 

extern std::ofstream  * out;

class X{

public:

X() { *out << "hello" << std::endl;}

};

extern X *x;

class Initializer {

static int flag;

public:

    Initializer() {  

if (flag == 0) {

         out = new std::ofstream("data.txt");

          x= new X(); flag ++;

}

}

~Initializer() { if (flag != 0) {

           delete x; delete out; flag =0;

}

}

};

static Initializer init;

#endif

Solution2Use static objects inside functions

// first file in the project

#include <fstream>

std::ofstream & get_out();

class X{

 public:

        X() { get_out() << "hello" << std::endl;}

} x;

// second file in the project

#include <fstream>

std::ofstream & get_out() {

      static std::ofstream out("data.txt");

      return out;

}

 

Chapter11: References & the Copy-Constructor

1、References in C++

引用必须与一块内存绑定且不可改变,并在定义时完成绑定,不存在NULL reference

当访问一个引用对象时,事实上是在访问它所指的那块内存,故可以借此改变对象的值

2、References in functions

在函数对引用对象的任何改变都会引起函数外该参数的改变

当一个函数return a reference的时候,必须确保引用指向的object在函数结束后依旧存在,即该object不是局部变量和临时变量

int& h() {

   int q;

!return q;  //wrong!

}

int & k(){

   static int x;

  return x;  //ok

}

3、Constant references

对于built-in types:意味着不改变形参的值

对于user-defined classes:意味着只调用const member function,且不改变public数据成员

形参以const Typename&形式模拟C中的按值传递参数,且由于没有复制过程,这种形式比按值传递效率高

4、Pointer references

void f( int * & p ) { p++; }

int * ip;  f(ip);

使用引用使得语句更加简洁,易阅读

5、Copy-constructor:实现classpass-by-value

class X{

public

       X(const X&);

};

可能会在“=,()”,按值传递函数参数或返回值被调用

int main() {

  HowMany h("h");

  HowMany h2 = f(h); //call copy-constructor twice

}

6、Default copy-constructor

class没有copy-constructor时,编译器会自动合成一个copy-constructor

对于普通类(no class composition and inheritance)default copy-constructor只是进行字节拷贝

对于classes with composition or inheritancedefault copy-constructor会自动给每一个embedded object合成copy-constructor

7、Have your own copy-constructor

写自己的copy-constructor时,需要自己构建全部embedded object

default copy-constructor只能进行浅拷贝,自己写的copy-constructor才能实现深拷贝(针对class对象存在指针的情况)

如果不希望pass-by-value,则需将copy-constructor声明为private

8、Pointer to data members and member functions

Pointer to data members

X d;

X *dp=&d;

int X::*p=&X::a;

dp>*p=1; //the same as d.*p=1;

Pointer to member functions

X d;

   X *dp = &d;

void (X::*pmem)(int) const;

pmem = &X::h;

(dp>*pmem)(1); //the same as (d.*pmem)(1);

 

Chapter12: Operator Overloading

1、Operator overloading只对用户定义的类有用

Operator overloading为当前类的运算符赋予新的意义,增加程序的可读性

2、Syntax

Global functions

    Unary operator > one argument

    Binary operator >two arguments

Member functions

    Unary operator > zero argument

    Binary operator > one argument

3Constraints

不能通过结合两个已存在的运算符创造新的运算符

运算符的优先级和结合顺序不会改变

4、Function arguments and return value

Function arguments通常为const+=operator例外

Return valuereturn a non-const value(a+b).func()合法;return a const value(a+b).func()非法

5、iostreamoperator(<<>>)

friend ostream& operator<<(ostream& osconst Typename & T);

friend istream& operator>>(ostream& isTypename & T);

6、Necessary behavior of operator=

simple class:拷贝所有必要的信息

class with pointers

为指针所指对象分配新的内存;引用计数(reference counting)

7Automatic type conversion

constructor conversion X(int i);

函数存在于目标类中

不能够进行user_type>built-in type的转换

加入explicit关键字后,不能自动进行转换

operator conversion operator X()const { return X; } 

函数存在于源类中

能够进行user_type>built-in type的转换

 

Chapter14: Inheritance & Composition

1、Rules for access control

class B: public D{ //inheritance access control

public: //class access control

       int a;

};

继承access control并不影响派生类成员函数对数据成员的访问,只对外部函数有影响

protected概念上是指在被派生类中允许被访问

最终在派生类中的访问权是access path上范围最小的

默认继承access controlprivate

class B {

public: int pub;

protected:    int pro;

private:      int pri;

};

Bpublic>D1protected>D2 D2可以访问pubpro

Bprotected>D1protected>D2 D2可以访问pubpro

Bprivate>D1protected>D2 pubpropri D2均不可访问

2、The initialization list

class C : public B {

A a;

public:

   C(int ii) : B(ii), a(ii) { }

};

AB存在default constructor,不需要在initialization list显式调用,即C(int ii){}

3、Order of constructor & destructors calls

constructor calls

从继承树的根部开始构造;在每个水平上,基类的构造函数先被调用,然后根据声明顺序调用成员对象的构造函数

destructor calls

constructor的调用顺序相反

4、Name hiding

当在一个派生类中重定义一个基类的函数名时,基类中所有相同函数名的函数在派生类中都会丢失

class Base {

public:

   int f( ) const {   

     cout << "Base::f()\n"; 

     return 1; 

   }

   int f(string) const { return 1; }

   void g( ) {}

};

 

class Derived : public Base {

public:

   void g() const {}   

};   // both f are available, g() is unavailable

5、Special points

constructorsdestructorsoperator=不能继承

static member functions在继承上和non-static member function是相同的,但static member functions不能被声明为virtual函数

6、Choosing composition vs. inheritance

composition: has-a relationship

inheritance: Is-a relationship

private Inheritance可以用private composition代替

class A{

public:

void print(){}

};

class B{                                                                 class C: private A{

A a;                                                                        public:

public:                                                                         using A::print;

       void print(){ a.print(); }                                    };

};

以上BC是等价的

7、Upcasting(Instrument>Wind只发生在public继承和protected继承的派生类中)

由于Wind是一种Instrument,故Wind对象能调用Instrument的所有成员函数;任何以Instrumentargument的函数都可以接受一个Wind对象,当这个argument为指针或引用时,会导致upcastingupcasted pointer or reference会丢失派生类的信息

enum note { middleC, Csharp, Cflat }; 

class Instrument {

public:

   void play(note) const  { }

};

class Wind : public Instrument { };

void tune(Instrument& i) {

   // ...

   i.play(middleC);

}

int main() {

   Wind flute;

   tune(flute); //call Instrument’s play()

}

upcasting对于派生类的copy-constructor的定义很有用

class Derived : public Base {

    A a;

public:

   Derived(const Derived& d): Base(d), a(d.a) {} //copy-constructor

}; 

 

Chapter15: Polymorphism & Virtual Functions:

(upcasting + virtual functionLate Binding>Polymorphism)

1、Early binding & Late binding

Early bindingcompile binding (function name overloading)

编译器在编译阶段确定每个对象调用的函数的地址 (14.7例子为Early binding)

Late bindingdynamic bindingruntime binding

编译器在运行阶段根据每个对象的实际类型确定对象调用的函数的地址

enum note { middleC, Csharp, Cflat }; 

class Instrument {

public:

   virtual void play(note) const  { }

};

class Wind : public Instrument { };

void tune(Instrument& i) {

   // ...

   i.play(middleC); //Late binding

}

int main() {

   Wind flute;

   tune(flute); //call Wind’s play()

}

对于类对象,编译器只会使用Early binding;对于类对象的指针或引用,编译器才可能使用Late binding

2、Virtual function

只有成员函数能声明为virtual,函数定义不需要加virtual关键字

基类函数为virtual时,派生类中该函数都为virtual

virtual function是实现Late binding的前提

3、C++中是通过VTABLEVPTR实现Late binding

对于每个包含virtual functionclass,只有一个VTABLE会被构建

类对象并不包含VTABLE,但每个类对象用一个各自的VPTR

VPTR记录了类对象的类型信息,VPTRconstructor中初始化

函数的地址以相同的顺序保存在VTABLE

4、Abstract base classes and pure virtual function

abstract base class:为派生类提供共同的接口;不能创建其类对象;至少含有一个pure virtual function

pure virtual function:形如virtual void print()=0;

类构建时,VTABLE中函数的顺序已经安排好,但没有任何pure virtual function的地址会被保存

基类pure virtual function在派送类中必须进行定义

派生类可以自主添加另外的virtual functioncommon function,派生类新定义的virtual function从当前派生类开始为virtual,原基类同名函数不为virtual

5、Object slicing

当将派生类对象按值传递给基类对象时,会得到一个真正的基类对象,此时会发生object silcingVPTR会改变,故对于类对象,编译器只会使用Early binding

6、Name hiding for virtual functions

重载虚函数不能够改变返回值类型,但可以有不一样的参数

7、Constructordestructor and virtual function

由于没有VPTR(VPTRconstructor中初始化),故不能够把constructor定义为virtual

应该将destructor定义为virtual,此时delelte an upcasted pointer才能正确析构

在任何constructordestructor中,调用virtual function时为Early binding,这是由于派生类对象还未被构建或已被析构

8、Downcasting

Circle * pc = new Circle;

Shape *ps = pc;

pc =dynamic_cast<Circle*> ps;

只有当ps原本就指向一个Circle对象时,downcasting能成功

 

Chapter16: Introduction to Templates

1、Use template to construct general vector

template<class T> //parameterized type

class X{

       ...

};

X<int>, X<double> ...

2、Multiple parameters & Default parameters

template<class T, int size> //multiple parameters

class Stack{

       T stack[size];

       ...

};

Stack<int, 100> s, Stack<string, 20> ...

template<class T, int size=100> //default parameters

3、Containera data structure whose length can vary automatically at runtime

容器有自动调节容量大小的函数

4、Introduction of iteratorsa smart pointer

iterator提供了遍历container和访问container中元素的接口

所有的iterator有共同的接口:begin(), end(), ++, --, *, >

各容器的iterator通常作为其nested classcontainer中声明实现

template<class T, int size=100>

class Stack{

T stack[size];

       int top;

public:

       class iterator;

       friend class iterator;

       class iterator{

              Stack& s;

              int dex;

       public:

              iterator(Stack& st): s(st), index(0){}

              iterator(Stack& st, bool): s(st), index(s.top){}

              ...

       };

...

};

5、The typename keyword

two condition

Used for a name which depends on a template argument

template<typename T>

class X{

...

};

The type is nested within the template argument

template<class T>

class X{

typename T::iterator itr;

...

};

6、Function templates

一种可以处理多种数据类型的函数;函数第一次调用之前模板类型不会确定

template <typename T>

T array_sum(T * p, int n){

T sum=0;

for (int i=0; i<n; i++) 

        sum += *p++;

        return sum;

}

int main (){   

        int ia [3]={1, 2, 3};

        double da [4]={1, 2, 3, 4};

        cout<<array_sum<int>(ia,3);

        cout<<array_sum<double>(da,4);

}

7、Automatic type deductioncalling arguments>template type

typevalue都可以进行推断,推断出的typevalue需与其他调用的参数一致

class templates不能进行type deduction

template <typename Dest, typename Src>

Dest implicit_cast (Src s) 

return s;

}

int main(){

int i;

        !implicit_cast(i); //no have enough argument

        implicit_cast<double>(i);

        implicit_cast<char,double>(i);

        implicit_cast<char*, int> (i); //int can’t be cast to char*

}

8、The moment of instantiation

对于function template,在调用函数时instantiated

对于class template,每个成员函数都在调用时才instantiated

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值