c++ primer 面向对象编程笔记

来源:互联网 发布:唐山直销软件 编辑:程序博客网 时间:2024/05/01 04:22
1)          动态绑定

C++中,通过基类的引用(或指针)调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象。

 

(2)          虚函数

1)       保留字virtual只在类内部的成员函数声明中出现,不能用在类定义体外部初相的函数定义上。

 

2)       派生类中虚函数的声明必须与基类中的定义完全匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数可以返回基类函数类型的派生类的引用(或指针)

1
2
3
4
5
6
7
8

class Base 

    virtual Base &f(); 
}; 
class Derive: public Base 

    Derive &f(); 
}; 

 

3)       默认实参

1、 通过基类的引用或指针调用虚函数时,默认实参是基类声明中的值

2、 通过派生类的指针或引用调用虚函数时,则默认实参是派生类声明中的值

这里就会产生一种情况,通过基类的引用或指针调用,而实际上执行的是派生类中定义的版本,而默认实参确实基类定义的参数

01
02
03
04
05
06
07
08
09
10
11
12

class Base 

public
    virtual void f(int x = 1); 
}; 
class Derive: public Base 

public
    void f(int x = 2); 
}; 
Base *pBase = new Derive; 
pBase->f(); //
默认实参x=1,调用Derive::f() 

 

(3)          protected成员

1)       private成员一样,protected成员不能被类的用户访问;

2)       public成员一样,protected成员可被该类的派生类访问;

01
02
03
04
05
06
07
08
09
10
11
12
13

class Base 

protected
    int x; 
}; 
class Derive: public Base 

    void f(Derive &d, Base &b) 
    { 
        d.x = 1
//      b.x = 1; //error 
    } 
}; 

 

(4)          作用域

在继承情况下,派生类的作用域嵌套在基类作用域中。在基类和派生类中使用同一名字的成员函数,在派生类作用域中派生类成员将屏蔽基类成员,即使函数原型不同

01
02
03
04
05
06
07
08
09
10
11
12

class Base 

public
    void f(); 
}; 
class Derive: public Base 

public
    void f(int x); 
}; 
Derive *d; 
//d->f();    //error 

 

   解决的方法有两种,第一种是显示使用作用域操作符,第二种使用using声明获得重载函数的行为一个using声明只能指定一个名字,不能指定形参表。有一种特殊情况,如果using声明的函数原型在派生类中已有定义,则派生类中的函数依然会屏蔽基类中的函数。

01
02
03
04
05
06
07
08
09
10
11
12

//方法一:显示使用作用域 
d->Base::f(); 
 
//
方法二:使用using声明 
class Derive: public Base 

public
    using Base::f; 
    //... 
}; 
Derive *d = new Derive; 
d->f(); 

 

   using声明的另一种作用是改变继承成员的访问级别,但不能使访问级别比基类中原来指定的更宽松

01
02
03
04
05
06
07
08
09
10

class Base 

public
    void f(); 
}; 
class Derive: private Base 

public
    using Base::f;    //
这里的f函数恢复为public 
}; 

 

      对于虚函数,有两个原则:1、只有成员函数中的代码才应该使用作用域操作符覆盖虚函数机制;2、派生类虚函数调用基类版本时,只能显式使用作用域操作符。

 

(5)          派生类到基类转化的可访问性

要确定到基类的转换是否可访问,可以考虑基类的public成员是否可访问,如果可以,转换是可访问的,否则转换是不可访问的。

这里可以这样理解,如果在派生类中不能访问基类的public成员,则说明派生类中不能访问基类的任何成员(派生类中对基类那部分完全不可见且不可操作),所以在该派生类中任何出现基类类型的成员函数(输入形参或返回类型)、成员变量都是错误,这里包括了从该派生类到基类的转换函数,所以不存在这样的转化函数(无论是默认或自己定义),也即不可访问的。

01
02
03
04
05
06
07
08
09
10
11

class A {}; 
class AA: private A {}; 
class AAA: public AA 

    ::A m;  //ok 
//  A m;    //error 
//  void f(const A &); //error 
}; 
 
AAA aaa; 
//A a = aaa;  //error 

 

(6)          构造函数和复制控制

1)       派生类构造函数不能初始化基类的成员且不应该对基类成员赋值,这样做会违反基类的接口,派生类应通过使用基类构造函数尊重基类的初始化意图。

2)       如果派生类定义了自己的复制构造函数,该复制构造函数一般应显式使用基类复制构造函数初始化对象的基类部分

3)       如果派生类定义了自己的赋值操作符,则该操作符必须对基类部分显式赋值

4)       即使析构函数没有工作做,继承层次的根类也应该定义一个虚析构函数。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19

class A 

public
    A &operator=(const A &m); 
    virtual ~A() {}    //
虚析构函数 
}; 
class AA: public A 

public
    AA(AA &m): A(m)    //
复制构造 
    { 
        //... 
    } 
    AA &operator=(const AA &m)    //
赋值操作符 
    { 
        A::operator=(m); 
        //
或者直接对基类成员赋值 
    } 
}; 

 

(7)          部分知识点

1)       如果需要声明一个派生类,则声明包含类名但不包含派生类列表。

2)       使用class保留字定义的派生类默认具有private继承,用struct保留字定义的类默认具有public继承。

3)       基类的友元对从该基类派生的类型没有特殊访问权限

原创粉丝点击