勿在浮沙筑高台

1.头文件中要写#ifndef,#define,#endif,如:

#ifndef __COMPLEX__  
#define __COMPLEX__  
// implement  
#endif  

2.构造函数尽可能使用初始化列表,而非在{}内初始化,如:

complex(double r=0, double i=0):re(r), im(i) {}  

3.类中不会修改成员变量的函数使用const修饰,C++中只有被声明为const的成员函数才能被一个const类对象调用(非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员)。当成员函数的const和non-const版本同时存在,const对象只会调用const版本,non-const对象只会调用non-const版本。
const对象调用非const成员函数则会出错 ,因此对于不修改成员变量的成员函数请使用const修饰。

// const成员函数保证不更改数据  
// non-const成员函数不保证数据不变  
double real() const { return re; }  

// class template std::basic_string有两个成员函数  
charT operator[] (size_type pos) const  
{... /* 不必考虑COW */}  
reference operator[] (size_type pos)  
{.../* 必须考虑COW */}  
// COW:Copy On Write  

4.函数传参时, 传递引用效率更高 ;如果可以的话,返回值也尽量返回引用
5.相同class的各个对象互为friends(友元)
6.操作符重载两种方法:成员函数/非成员函数。

// 成员函数(可以使用this指针)  
inline complex& complex::operator += (const complex &r)  
{  
    return __doapl(this, r);  
}  

// 非成员函数  
inline complex operator + (const complex& x, const complex& y)  
{  
    return complex(real(x) + real(y), imag(x) + imag(y));  
}  

7.<<>>操作符重载只能使用非成员函数,不能使用成员函数重载,因为它们的左值cout是系统定义好的。

ostream& operator << (ostream& os, const complex& x)  
{  
    return os << '(' << real(x) << ',' << imag(x) << ')';  
}  

8.三个特殊函数:拷贝构造函数、拷贝赋值函数、析构函数,如果类的数据中含有指针需要设计这三个函数。

class String  
{  
public:  
    String(const char* cstr = 0);  
    String(const String& str);  
    String& operator = (const String& str);  
    ~String();  
private:  
    char* m_data;  
};  

9.拷贝赋值函数需要检测自我赋值。

inline String& String::operator=(const String& str)  
{  
    if(this == &str)  // 检测自我赋值  
        return *this;  
    delete[] m_data;  
    m_data = new char[strlen(str.m_data) + 1];  
    strcpy(m_data, str.m_data);  
    return *this;  
}  

10.new先分配存储空间,再调用构造函数;delete先调用析构函数,再释放内存。
11.使用delete只会调用一次析构函数,调用delete[]会对每个数组元素调用析构函数。
12.类的非静态成员函数隐含有this参数,而静态成员函数不含有this参数。静态成员函数只能操作类的静态成员数据。
13.类的静态变量在使用之前需要在类外定义。

class Account {  
public:  
    static double m_rate;   // 声明  
    static void set_rate(const double& x) { m_rate = x; }  
};  
double Account::m_rate = 8.0; // 定义  

14.类模板:使用时明确指出typename

template<typename T>  
class complex  
{  
public:  
    complex(T r=0, T i=0):re(r), im(i) {}  
    ...  
private:  
    T re, im;  
    ...  
}  

{  
    complex<double> c1(2.5, 1.5);  
    complex<int> c2(2, 6);  
    ...  
}  

15.函数模板:使用时由编译器根据实参推导模板类型。

// <class T> 与 <typename T>是等价的  
template<class T>  
inline const T& min(const T& a, const T& b)  
{  
    return b < a ? b : a;  
}  
// 使用函数模板时由编译器推导类型  
stone r1(2, 3), r2(2, 2), r3;  
r3 = min(r1, r2);  

16.类与类之间的关系一:Composition(复合,表示has-a,一个类中有另一个类)。Composition关系下的构造函数由内而外执行(先调用Component的构造函数,再调用Container本身的构造函数),析构函数则是由外而内。

// Composition  
class Container {  
    ...  
    Component component;  
};  
// 模拟的构造函数执行过程(此过程由编译器处理)  
Container::Container(..): Component() {}  
// 模拟的析构函数执行过程  
Container::~Container(..) {... ~Component();}  

17.类与类之间的关系二:Delegation(委托,composition by reference,一个类拥有另一个类的指针),含有另一个类指针的类的实现永远通过另一个类实现,可以使得这个类对外的接口保持不变。

class StringRep;  
class String {  
public:  
    String();  
    ...  
private:  
    StringRep* rep; // pimpl  
};  

18.类与类之间的关系三:Inheritance(继承,表示is-a)。子类的构造函数首先调用父类的构造函数,再执行自己的构造函数;子类的析构函数先执行自己,再调用父类的析构函数。 父类的析构函数必须是virtual。

class Derived:public Base {  
...  
};  

19.non-virtual函数:不希望子类重新定义(override,复写)的函数;virtual函数:希望子类重新定义的函数,不过它已有默认定义;pure virtual函数:希望子类一定要重新定义它,它没有默认定义。

class Shape {  
public:  
    // 纯虚函数pure virtual  
    virtual void draw() const = 0;  
    // 普通虚函数impure virtual  
    virtual void error(const string& msg);  
    // 非虚函数non-virtual  
    int objectID() const;  
    ...  
};  

20.Delegation + Inheritance

class Subject {  
    int m_value;  
    vector<Observer*> m_views;  
public:  
    void attach(Observer* obs) {  
        m_views.push_back(obs);  
    }  
    void set_val(int value) {  
        m_value = value;  
        notify();  
    }  
    void notify() {  
        for(int i = 0;i < m_views.size(); ++i) {  
            m_views[i]->update(this, m_value);  
        }  
    }  
};  
class Observer {  
public:  
    virtual void update(Subject* sub, int val) = 0;  
};  

革命尚未成功,同志仍需努力

21.转换函数(conversion function)可以在使用一个类时直接把它转换为另一种类型。

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  

22.non-explicit-one-argument constructor:构造函数可以只接受一个实参,这样可以把这个实参类型转换为这个类的类型。

class Fraction {  
public:  
    // non-explicit-one-argument ctor  
    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 d = f + 4; // 调用non-explicit ctor将4转换为Fraction类型,然后调用operator+  

23.explicit-one-argument constructor:只要显式使用构造函数的语法才会调用的构造函数,编译器不会进行non-explicit-one-argument那样的转换。

class Fraction {  
public:  
    // explicit-one-argument ctor  
    explicit 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 d = f + 4; // [Error]不会把4转为Fraction  

24.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;  
    ...  
};  

25.pointer-like classes之迭代器。

// 迭代器中对指针操作符*和->的重载  
// reference  
T& operator*() const {  
    return (*node).data;  
}  
// pointer  
T* operator->() const {  
    return &(operator*());  
}  

26.variadic templates(since C++11):数量不定的模板参数。...就是一个所谓的pack(包),可以用在模板参数、函数参数类型、函数参数中,sizeof...(args)可以得到包中参数数量。

void print() {}  
template<typename T, typename... Types>  
void print(const T& firstArg, const Types&... args)  
{  
    cout << firstArg << " " << sizeof...(args) << endl;   
    print(args...);  
}  
int main()  
{  
    cout << __cplusplus << endl;  
    print(7.5, "hello", bitset<16>(377), 22);  
    return 0;  
}  
/*  
201103  
7.5 3  
hello 2  
0000000101111001 1  
22 0  
*/  

27.vptr(virtual pointer)虚指针在拥有虚函数的类中存在,其指向vtbl(virtual table)虚表,虚表中存放的是指向虚函数的指针。当使用 指针 调用 虚函数 ,且这个指针类型是指针指向元素的父类类型( 指针向上转型 ),此时编译器会使用动态绑定。通过这种虚继承,可以实现面向对象中的 多态 机制。

评论

还没有登陆?评论请先登陆注册
123456789 2020-06-05 20:29:38

a

 联系方式 contact me

Github
Email
QQ
Weibo