Sunday, February 06, 2011

C++笔记

继承
继承方式:
  • public: 可以被任何人访问
  • protected:可以被本类以及子类访问
  • private: 只允许本类访问
虚继承: C++支持多重继承,也就是一个类可以同时有多个直接继承的基类.如果碰到如下的继承方式
A
| \ 
B  C
 \ |
   D
也就是类B和C分别继承A,同时D继承了B和C.这时候我们就需要使用以下的方法来让B和C虚继承A:
class A {
public:
    void printA() {}
};
class B:virtual public A;
class C:virtual public A;
class D:public B,public C;
如果B和C不使用虚继承,那么最终派生类D中就有可能含有两个A的作为基类,从而产生了二义性.


类型转换


C中的类型转换可以将一种类型的指针转换为任何其他类型的指针, 比如
ptr2 = (ClassFoo*) &obj;
C++中,可以有更多的选择
  • dynamic_cast <new_type> (expression) 可以将子类指针转化为父类指针
  • static_cast <new_type> (expression)可以将相关联的类指针互相转化, 比如子类指针到父类指针,或者父类指针到子类
  • reinterpret_cast <new_type> (expression)可以将任何类型指针相互转化
  • const_cast <new_type> (expression)保证转化后的指针是const


构造函数


派生类构造函数的调用顺序 如果基类的构造函数没有参数, 那么在进入派生类构造函数之前,基类构造函数将会被自动调用. 如果基类的构造函数有参数,那么派生类需要显式的调用基类构造函数.也就是所谓的"constructor chaining". 并且由于支持多继承,C++不能象Java那样使用super()
class Derived : public Base
{
    public:
        Derived(int foo, int bar)
        : Base(foo), bar_(bar)   // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};
explicit 关键字explicit可以修饰构造函数, 使得"converting constructor"非法.
class A {
public:
    A(int);
};
class B {
public:
    explicit B(int);
};
main() {
    A a1 = 37; // 合法
    A a2(37);  // 合法
    B b1 = 37; // 非法
    B b2(37);  // 合法
}


析构函数


virtual 有时候类的析构函数会被virtual关键字修饰, 这样可以强迫该类的派生类对象析构的时候使用派生类的析构函数而不是基类的----哪怕该对象是当做一个基类来被引用. 注意,一般说来当一个类含有虚方法的时候, 原则上就应该让析构函数为虚----因为虚方法意味着这个类多半是作为interface来使用.
class Base {
    virtual ~Base();
};
class Derived : public Base {
    ~Derived();
};
int main()
{
    Base* p = new Derived;
    delete p; // virtual destructor used to ensure that ~Derived is called
}


成员变量/函数


virtual 使用该关键字最主要的目的在于,当使用父类型的指针指向其子类的实例的时候,可以通过通过父类的指针调用实际子类的成员函. 编译器看到virtual关键字修饰的函数时,就会使用动态绑定(dynamic binding)在运行期决定函数的入口,而不是像普通的未被virtual关键字修饰的那种函数那样在编译期就静态绑定(static binding)函数入口.比如执行下面的代码
class Base {
    public:
        void foo() { cout << "Base::foo" << endl; }
        virtual void vfoo() { cout << "Base::vfoo" << endl; }
};
class Derived: public Base{
    public:
        void foo() { cout << "Derived::foo" << endl; }
        void vfoo() { cout << "Derived::vfoo" << endl; }

};
int main() {
    Base *p;
    p = (Base*) new Derived;
    p->foo();
    p->vfoo();
}
输出为
Base::foo
Derived::vfoo
static 静态成员变量是为了让同一种类的不同实例共享数据.该静态成员变量的初始化在类外,例如
int MyClass::MyStaticVar = 1;
静态成员函数与普通成员函数区别在于没有this指针,所以无法得知是针对哪个实例的操作.因此静态成员函数只能访问静态成员变量. 静态成员函数也不能被virtual, const, volatile, 以及const volatile所修饰. constconst修饰的成员函数无法是read-only, 不能修改任何调用者的成员.
class Test {
    public:
        int test;
        void foo() {test = 1;} // 合法
        void cfoo() const {  test = 1;} //非法
};
const不能修饰构造和析构函数,并且不能被非const函数重载.


inline inline函数在编译期被替换至caller的代码中,所以节省了函数调用的开销.在 C++当中,尽量用inline而不是#define.inline的成员函数定义必须在头文件中,

ref: http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.1


变量


const 用于声明那些编译期就可以确定值的变量,这样的编译器可以比用C中的#define替换要知道更多的信息,从而给出更给力的报错信息.用const声明的变量必须立即赋值--否则之后无法对该变量进行赋值或者修改.
int main() {
   const int i = 5;
   i = 10;   // 非法
   i++;   // 非法
}

ref http://duramecho.com/ComputerInformation/WhyHowCppConst.html
volatilevolatile关键字修饰一个变量是为了通知compiler不要对这个变量进行优化----因为这个变量可能被其他thread或者被系统使用.


模版(Template)


模版类的实现必须包括在.h文件中,否则编译的时候会找不到模版类的实现.
具体原因参见http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12
参数 Template可以使用class,typename参数,也可以使用非类型变量比如一个int或者char变量
template <class C, typename T, int N>
class myclass {
    T seq[N];
};
继承模版类
template <typename T>
class Base {
    T seq[N];
};
template <typename T>
class Derived: public Base< T > {
};

No comments: