C++ primer 随笔(4) (面向对象编程)

protect成员

1.像private成员一样,protected成员不能被类的用户访问。
2.像public成员一样,protected成员可以被该类的派生类访问。
3.派生类只能通过派生类对象访问其基类的protected成员,而不能直接通过基类访问其派生类。

友元类

声明

class B;//这里是前向声明

class A{
friend B;friend class Bpublic:
 
private:

protected:
    int i;
}

class B
{
public:
int f(A a){return a.i;}//声明为友元,可调用A类中的private与protected元素
}

当把B声明为A的友元类时,可以在B中实例化A,比如在B的私有成员中声明A的实例a,并可借助a访问A的实例a的一切私有成员。
友元关系不能传递
友元关系没有反身对称性
友元关系不能继承

友元函数

友元函数需要实例化A为a,并将其传入友元函数,友元函数中可以调用A类的实例化a中的所有私有成员。

//这里是全局友元函数
class A{
friend void printxy(A &a);
public:

private:

}

成员友元函数定义如下:

//这里是将B的成员函数声明为A的友元
class A {
friend void B::printxy(A &a)
public:
private:
int i;
}
class B{
public void printxy(A &a)
{cout<<i<<endl;}//B中可以调用A中的私有数据成员
}

派生类

class 派生类名:public/protected/private 基类名

public公用继承:各类保持自己的访问级别,派生类及其函数都不能访问私有成员。
protected受保护继承:基类的public与protected成员在派生类中为protected成员。
private私有继承:基类的所有成员在派生类中为private成员,派生类成员函数可以访问基类中的公有及保护成员,而不能访问私有成员;而依靠实例化的派生类成员,不能访问基类中的任何元素。

派生类与基类的常见关系:

  1. is a
  2. has a

若不声明,则默认缺省的是私有继承

派生类与虚函数

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

被派生类用作基类的的类必须是已定义的

只有声明而没有定义的类不能被用做基类
派生类可迭代定义,即派生类可以有自己的派生类

virtual声明的虚函数

动态绑定必须满足的条件:
1.只有指定为虚函数的成员函数才能进行动态绑定。
2.必须通过基类类型的引用或指针进行函数调用。

将基类类型的引用或指针绑定到派生类对象对基类对象没有影响
关键:虚函数支持在运行时确定对不同类型函数的调用

派生类虚函数调用基类版本时,必须显式地使用作用域操作符,如果派生类函数忽略了这样做,函数调用时则会确定是一个自身调用,导致无穷递归。

当要复制一个未知类型的对象时,应将该函数定义为虚函数,返回一个virtual 的引用基类。

派生类的构造函数

派生类的构造函数中,只需构造派生类中比基类多的元素即可,之后他会调用基类的构造函数完成派生类函数的初始化。也可以在派生类的构造函数中显式地调用基类的构造函数来实现派生类的初始化。

class Base{...};
class Derived:public Base
{
public:
    Base::Base(const Base&)
    Derived(const Derived &d):
           Base(d)
}

基类的函数隐藏(c++中的特定机制)

当基类中有函数重载时,而派生类中有独立的同名函数(它们仅仅是同名关系),这种情况下编译器会将基类中同名函数全部隐藏,该名下的函数仅剩派生类中的函数。

返回一个引用的函数

只在开头类型声明处加上取引用符号,在return后面直接加要返回的值,不须再加&或 *。同时应注意的是:返回的引用不能是属于函数的局部变量,而必须是全局变量。使用该函数时,也是直接使用,而不用加引用&或解引用符号 *

将一个较大的对象传入函数

在将一个较大的对象传入函数时,一个比较好的做法是,传一个const指针进去。
例如const int& x
对象较大时,传值过程效率较低,直接传引用会提升效率。

static关键字

声明函数具有静态生存期,尽管只有在某个局部函数中才被使用,但该变量的内存空间一直被分配在固定位置,直到整个程序结束,该空间才被释放。
静态数据成员依赖于类,而不依赖于对象。
生存期与类相同,而不是与特定对象相同。
静态数据成员初始化一般在类外
静态成员函数无法调用非静态的数据成员,因为非静态的数据成员不是属于类,而是属于对象的,静态成员函数无法得知调用的非静态数据成员属于哪个对象。

const使用位置

const int * p1=&a; //指针指向的内容不能变化 p1指向const int
int * const p1=&a; //指针指向的地址不能变化const p1 指向int
const int
const p1=&a;

const常成员函数

声明格式如下:
类型说明符 函数名(参数)const;
具体内部实现:隐含一个const声明的this指针
表明该函数不会对对象中的内容做修改
const成员函数可以构成函数重载,只要声明了const类型变量,该成员的一切函数操作默认视作调用重载后的常成员函数。
const A a;

操作符重载

对于操作符=,[ ],(),->,.等操作符需要定义为成员变量。
而对于其他算术与关系操作符,最好定义为非成员变量,为了保证它能够访问该类的私有数据,应将其声明为该类的友元,但若该操作符不会访问该类的私有成员,那就不必将其声明为友元。
声明形式如下:

class A
{
friend istream& operator>>(istream &in, A &a);
}
A operator+(const A &a,const A &b);

应当注意的是:如果类中需要动态分配内存,则必须定义它的拷贝构造函数与赋值操作符,并且赋值操作符中一定要对自赋值做检查并释放旧内存,否则会把动态分配的空间全部释放。

函数对象

函数对象的好处是,在多次调用的过程中,其私有成员是共享的,不必使用全局变量。在使用时,如同函数一样调用。

class A
{
public:
     A(size_t val=0):bound(val){}
     bool operator() (const string &s)//这里是重载操作符()
     {return s.size()>=bound;}
private:
     int bound;
}

//调用时:
string s;
A(s);

多继承

当一个派生类继承多个基类时,可使用下面的语句

class farmworker:public worker,public farmer{
}

继承时依次序进行初始化。

菱形继承导致的重定义

若一个多继承派生类的多个基类又继承于同一基类,则会导致重定义的问题。
解决这样问题的手段是:采用宏定义
在被重定义的基类头文件中写

#ifndef  MYCLASS_H
#define  MYCLASS_H


#endif

基类被多次继承可用虚继承在对重用基类的继承时加上virtual即可

class Worker : virtual public Person
class Children : virtual public Person
class ChildLabourer:public Worker,public Children

虚函数的实现方式

虚函数的实现采用虚函数表的方式实现,虚函数表中是指向函数的指针,当未加virtual时,该指针指向基类中的函数代码段。加上virtual后,该派生类的虚函数表中的指针被修改,以一种覆盖机制,指向该派生类自己的成员函数,当调用虚函数时,该指针动态绑定到派生类的成员函数,实现函数调用。

纯虚函数

格式:
virtual void work()=0;
若某个函数的存在只是为了让派生类继承,而不做其他任何操作,则应将其声明为纯虚函数。
这样的纯虚函数为派生类提供了可以覆盖的接口,但该函数在基类中绝不会调用。并且用户不能创建该基类的对象。
包含纯虚函数的类是一个抽象类,该类将不能进行实例化,用户将不能创建该类的对象。

接口类

仅含有纯虚函数的类叫做接口类,该类中无数据成员,只有成员函数,而且所有成员函数全为纯虚函数。

RTTI(运行时类型识别)

一些情况下我们只有在程序运行时才知道传入的数据类型(常用于用户交互界面得到的数据),为了使该对象执行与其数据类型对应的操作,我们常用RTTI的方式完成运行时类型识别。

void f (Person *p) {
if(typeid(*p)==typeid(Worker))
   {
         Worker *wo=dynamic_cast<Worker*>(p);//这里做了强制转换,将其从基类确定成了Worker类
         p->doA();
   }
else
   {
         do B();
   }
}

猜你喜欢

转载自blog.csdn.net/lxx909546478/article/details/88375949