一.友元
友元提供了访问类的私有数据成员的机制
using namespace std;
class c2;
class c1
... {
int i;
public:
void set(int j)...{i=j;}
bool allzero(c2);
} ;
class c2
... {
int i;
public :
void set(int j)...{i=j;}
friend bool c1::allzero(c2);
} ;
bool c1::allzero(c2 c)
... {
if(this->i==0 && c.i==0)
return true;
return false;
}
int main()
... {
c1 cx;
c2 cy;
cx.set(0);
cy.set(0);
if(cx.allzero(cy))
cout<<"all zero"<<endl;
return 0;
}
2.友元运算符重载
对于operator+,两种重载方式:
... {
three_d temp;
temp.x=x-t.x;
temp.y=y-t.y;
return temp;
}
对于上述的这种方式,a=a+10可以运行,但a=10+a不行.
... {
three_d temp;
temp.x=t1.x-t2.x;
temp.y=t1.y-t2.y;
return temp;
}
对果用两个友元函数来重载+,则上述问题可以避免.一种是+运算符函数处理对象+函数,另一种运算符处理"整数+对象".使用友元重载+(或其他二元操作数)允许内建类型出现于运算符的左边或右边.
二.访问权限的控制
1.public,private,protected
第一:private,public,protected方法的访问范围.
private: 只能由该类中的方法访问,不能被该类的对象访问.
protected: 可以被该类中的方法和其友元函数访问,但不能被该类的对象访问
public: 可以被该类中的方法和其友元函数访问,也可以由该类的对象访问
第二:类的继承后方法属性变化:
使用private继承,父类的所有方法在子类中变为private;
使用protected继承,父类的protected和public方法在子类中变为protected,private方法不变;
使用public继承,父类中的方法属性不发生改变;
基类的public成员能够被程序中所有函数访问,private成员只能被基类的成员函数和友元访问。protected访问是public访问和private访问之间的中间层次。基类的protected成员只能被基类的成员和友元以及派生类的成员和友元访问。派生类成员简单地使用成员名就可以引用基类的public成员和protected成员。注意protected数据破坏了封装,基类protected成员改变时,所有派生类都要修改。
公有派生类的对象可作为其相应基类的对象处理,这使得一些有意义的操作成为可能。例如,从某个特定基类派生出来的各种类,尽管这些类的对象彼此之间互不相同,但是仍然能够建立这些对象的链表,只要把这些对象作为基类对象处理就可以了。然而反过来是不行的,基类的对象不能自动成为派生类的对象。
三.构造函数,析构函数的调用顺序.
using namespace std;
class base
... {
public:
base()...{cout<<"base constructor"<<endl;}
~base()...{cout<<"base destructed"<<endl;}
} ;
class deliver: public base
... {
public:
deliver()...{cout<<"deliver constructor"<<endl;}
~deliver()...{cout<<"deliver destructed"<<endl;}
} ;
int main()
... {
deliver d;
return 0;
}
base constructor
base destructed
deliver constructor
deliver destructed
生成一个派生类对象时,如果基类有构造函数,则这个构造函数首先被调用,接着调用派生类的构造函数,派生对象删除时,析构函数首先被调用,接着调用基的析构函数.不同的是,构造函数按序,而析构接与派生相反的顺序执行.
四.虚基类
防止在MI中,派生类出现含义不清的基类变量.如在下面代码中:
using namespace std;
class base
... {
public:
int i;
} ;
class deliver1: public base
... {
public :
int j;
} ;
class deliver2: public base
... {
public:
int k;
} ;
class deliver3: public deliver1, public deliver2
... {
int sum;
} ;
int main()
... {
deliver3 d3;
d3.i=10;
d3.j=10;
d3.k=30;
d3.sum=d3.i+d3.j+d3.k;
return 0;
}
程序会报错,因为d3.i=10中,出现的i可能是deliver1的成员也可能是deliver2成员.
稍加修改
using namespace std;
class base
... {
public:
int i;
} ;
class deliver1: virtual public base
... {
public :
int j;
} ;
class deliver2: virtual public base
... {
public:
int k;
} ;
class deliver3: public deliver1, public deliver2
... {
public:
int sum;
} ;
int main()
... {
deliver3 d3;
d3.i=10;
d3.j=10;
d3.k=30;
d3.sum=d3.i+d3.j+d3.k;
return 0;
}
加上virtual后,作为virtual的基类继承保证了在任何派生类中只提供一个基类副本.
如果感兴趣,还可以看看上述两种方式的构造,析构顺序.
五.指向派生类的指针
基类指针可以指向派生对象的任何类型,但反过来不成立.
基类指针不能方问那些限制为派生类的元素.它可以访问由基类继承来的派生类元素.
如果想用基类指针来访问由派生类定义的元素,那么就必须把基类指针强制转化为派生类的指针,如:((deliver_class*)p)->show_title();