勿在浮沙筑高台
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)虚表,虚表中存放的是指向虚函数的指针。当使用 指针 调用 虚函数 ,且这个指针类型是指针指向元素的父类类型( 指针向上转型 ),此时编译器会使用动态绑定。通过这种虚继承,可以实现面向对象中的 多态 机制。
a
--
123456789
更改id为3
--
test
更改id为2
--
commentor
伪造名称???
--
hhh
伪造名称???
--
yayay