第05章 CORE C++_对象的创建和使用_继承_多态_析构_xxx_cast_友元_只读成员_静态成员_多重继承_虚继承_内部类

1.问题:要求写一个分数类,有分子分母,能设置分子分母,能够约分,能显示小数。

   思路:与上一章例程不同的是,在约分的过程中,先求最大公约数,然后得到约分后的分子和分母。

   代码:#include <iostream>  
using namespace std;  
class Fract{  
 int n;  
 int d;  
public:   
 void show(){  
cout << n << '/' << d << endl;  
}  
int maxdiv(int a, int b){//  
        cout << n << '/' << d <<',';//用于观察最大公约数的计算过程
 return a==0||b==0?a+b:maxdiv(b,a%b);  
}   
void reduce(){  
int div = maxdiv(n, d);  
if(div!=0){  
n /= div;  
d /= div;  
}  
}  
Fract(int n, int d=1)//构造函数  
:n(n),d(d)  
{
cout << n << '/' << d << endl;//用于观察构造函数对数据成员初始化过程  
reduce();  
}  
Fract():n(0),d(1){}//构造函数  
};  
int main()  
{  
Fract (3,6).show();
Fract (3,4).show();
Fract (3).show();
Fract ().show();
Fract f1;//如果没有参数可传,就相当于函数声明。创建对象时,如果没有参数可传递,不要加“()” 
}  

   解释:Fract (3,6)=>Fract(int n=3, int d=6)=>:n(3),d(6)=> maxdiv(3, 6)=>maxdiv(6, 3%6=3)=>maxdiv(3, 6%3=0)

=>a+b=3=>div=3=>n=3/3=1,d=6/3=2=>1/2
2.问题:临时空间,即构造函数与对应析构函数配对执行。请编例程。
思路:临时空间只能一次性使用,在那个语句后就立即释放
代码:#include<iostream>  
using namespace std;  
class A{  
int d;  
public:  
A(int d=0):d(d){cout<<d<<"A(int)/n";}  
~A(){cout<<d<<"~A()"<<endl;}  
};  
int main()  
{  
A a1;//直到碰到主函数‘}’,才有可能运行析构函数。如果多个构造函数,并且是按照一定顺序执行析构函数。  
A(5);// 可以看成强制类型转换:调用一次构造函数,就完成任务。 
A a3(3); //直到碰到主函数‘}’,才有可能运行析构函数。 
cout<<"-------------"<<endl;  
}  
解释:多个析构函数要执行时,遵守栈原理,即先进后出,后进先出的顺序。
3.问题:输出一个对象(名和地址)“结婚与”另一对象(名和地址);声明用已有对象初始化的对象;创造形参为对象,返回值也是对象的

函数。
思路:1.定义Person类:
1.1.定义数据成员:声明name,Person类指针对象lover
1.2.定义函数成员:1.构造函数及其类数据成员初始化,输出name及其地址
2.定义析构函数,输出name及其地址
3.定义marry函数:
3.1.形参为对象(这样,如果实参传到,就会再次调用构造函数,而这是非基本类型构造函数;等到marry函数执行完,就调用对应析构函数)
3.2.把收到实参对象重新初始化后地址存到调用对象的lover指针变量中
3.3.把当前地址传给实参对象的lover指针变量,输出调用对象的name和地址,传送实参对象name和地址(首数据成员地址与对象地址相同)
4.拷贝构造函数:1.即非基本类型构造函数,比如对象。其形参为引用,即把传送实参对象的地址传给形参对象(即实参地址==形参地址,如

果相关数据成员内容发生变化,两种调用同一个变化内容)(将会重新调用对应传送实参对象)
2.输出形参对于name和及其地址(即重新调用的实参对象地址)
3.把形参对象name前加“克隆”给实参name。(执行多少次非基本类型构造函数,加几个“克隆”给实参对象name)
4.初始化lover(因为拷贝构造函数也是构造函数嘛)
2.定义返回值也是对象的函数echo,形参也是对象。(即本函数在始末将各自调用一次构造函数,结束时会调用一次形参对象对应析构函数)
3.1.声明cgx对象,初始化为“陈冠希”
3.2.声明fr对象,初始化为“芙蓉”
3.3.cgx对象,调用person类marry函数成员,传送给形参,fr对象
3.4.声明fr2对象,初始化为fr对象(调用对象fr2的拷贝构造函数,在本程序中未再使用fr2对象,所以在主函数结束实调用其析构函数)
3.5.调用echo函数(因为它将调用两次拷贝构造函数,将会在echo函数结束时,调用析构函数依次执行。同一类的不同对象的成员调用会存放

在栈中。echo函数不是对象的函数成员,所以不在栈中,不遵守栈的顺序。)
4.自动依次调用剩下的对应析构函数
代码:#include<iostream>  
using namespace std;  
#include<string>  
class Person{  
string name;  
Person* lover;//这个数据成员其实没用上其值,虽然是使用了其变量。  
public:  
Person(string n):name(n),lover(NULL){  
cout<<name<<" 出生了,身份证号:"<<this<<endl;  
}  
~Person(){  
cout<<name<<" 千古了,身份证号:"<<this<<endl;  
}  
void marry(Person one){  
lover=&one;  
one.lover=this;  
cout<<name<<"(ID:"<<this<<")"<<" 跟"<<one.name  
<<"(ID:"<<&one<<")"<<" 相爱了!"<<endl;  
}  
Person(const Person& p){// 在向函数传递非基本类型的数据时候,尽量使用引用,如果不需要修改就加const 。// 也叫拷贝构造函数 

 
cout<<" 克隆"<< p.name <<",ID:"<<this<<endl;  
name="克隆"+p.name;  
lover=NULL;  
}  
};  
Person echo(Person p)// 注意了return 后的也是一个变量//可选代码,即函数echo 
{  
return p;// 注意了return 后的也是一个变量 
}   
int main()  
{  
Person cgx(" 陈冠希");  
Person fr(" 芙蓉");  
cout<<"---------------------"<<endl;  
cgx.marry(fr);  
Person fr2(fr);      
cout<<"---------------------"<<endl;
echo(fr);// 注意了return 后的也是一个变量//可选代码
cout<<"---------------------"<<endl;  
}  
补充:如果不选可选代码,结果将是如下。
陈冠希出生了,身份证号:0xbfd54ca0
芙蓉出生了,身份证号:0xbfd54c98  
--------------------- 
克隆芙蓉,ID:0xbfd54cb8
陈冠希(ID:0xbfd54ca0) 跟克隆芙蓉(ID:0xbfd54cb8) 相爱了!
克隆芙蓉千古了,身份证号:0xbfd54cb8
克隆芙蓉,ID:0xbfd54c90  
--------------------- 
克隆芙蓉千古了,身份证号:0xbfd54c90
芙蓉千古了,身份证号:0xbfd54c98
陈冠希千古了,身份证号:0xbfd54ca0  
解释:1.拷贝构造函数,注意形参传递的过程:如果自己没写,系统会自动产生一个拷贝构造函数,它会把形参对象的数据成员。 
逐个复制到指定对象中,但只复制数据成员。形参是独立与实参的一种变量,并不能认为形参就是实参。 
2.可选代码部分:返回值临时变量也是变量,是用return 后的数据初始化的返回的值也是一个临时对象。 
3.可选代码部分:为什么会产生“克隆克隆 芙蓉:ID:...”?形参会产生一个“克隆 芙蓉”,在echo函数结束前,还要执行一次拷贝构造函

数。因为echo函数未结束,没有对形参的“克隆 芙蓉”析构,还保留在fr.name地址中。而返回的还是原形参对象,再次执行拷贝构造函数时

,仍然会保存在fr.name地址中,所以就那啥了。然后才会结束echo函数,接着运行析构函数。
4.问题:编写一个时钟程序,指定时间开始计时,到指定时间输出“时间到”,继续开始计时 
思路:重点介绍继承部分代码的执行过程:
定义Alarm类:class Alarm :public Clock,这段代码表示定义继承关系的确立。然后执行构造函数,:atime(ah,am,as),Clock(h,m,s),表示

把接收到的对应实参调用构造函数Time(int h,int m,int s):h(h),m(m),s(s);另外对应实参则直接调用Clock类构造函数Clock(int h,int

m,int s):now(h,m,s)(他们是父子关系,不用通过对象间接调用),间接拥有了now对象。
代码:#include<iostream>  
using namespace std;  
#include<ctime>  
class Time{  
int h;  
int m;  
int s;  
public:  
Time(int h,int m,int s):h(h),m(m),s(s){}  
void advance(){  
if(++s>=60){  
s=0;  
if(++m>=60){  
m=0;  
if(++h>=24)  
h=0;  
}  
}  
}  
void nextsecond(){  
long start=time(NULL);  
while(time(NULL)==start);  
}  
void show(){  
cout<<'/r';  
if(h<10) cout<<0;  
cout<<h<<':';  
if(m<10) cout<<0;  
cout<<m<<':';  
if(s<10) cout<<0;  
cout<<s<<flush;  
}  
bool equal(const Time& t){//Time类中增加的代码//引用,执行时,t地址==atime地址  
return (h==t.h&&m==t.m&&s==t.s);  
}  
};  
class Clock{  
protected:// 对于继承它的类是开放的,对于其它则不然 
Time now;  
public:  
void run(){  
for(;;){  
now.nextsecond();  
now.advance();  
now.show();  
}  
}  
Clock(int h,int m,int s):now(h,m,s){}  
};  
class Alarm :public Clock{// 继承不是拷贝//增加了这部分内容,比上一章15题。 
Time atime;  
public:  
Alarm( int h,int m,int s,int ah,int am,int as  
):atime(ah,am,as),Clock(h,m,s){}// 只能在初始化类表传 
void run(){  
for(;;){  
now.nextsecond();//同理,now是Time类,所以可以调用Time类中成员函数  
now.advance();//同理,now是Time类,所以可以调用Time类中成员函数  
now.show();//同理,now是Time类,所以可以调用Time类中成员函数  
if(now.equal(atime))//同理,now对象调用Time类中equal成员函数:判断now对象时间==atime对象时间?true:false;  
cout<<"/n 时间到/n"<<endl;  
}  
}  
};  
int main()  
{  
Alarm a(23,59,50,23,59,55);//重点介绍这个类的构造函数执行过程  
a.run();  
}  
解释:1.形参和实参不是同一个 
2.继承:一个类中拥有另一个类的内容
3.Alarm类继承了Clock类,而Clock类有数据成员是Time类对象,同时Alarm类有数据成员也是Time类对象。所以从Alarm类角度看,既可以使用

Time类的成员,也可以用Clock类成员。
5.问题:有无实参子类初始化过程演示
思路:无
代码:#include<iostream>  
using namespace std;  
class Parent{  
public:  
Parent(int d){  
 cout<<"Parent(int)"<<endl;  
}   
Parent(){  
cout<<"Parent()"<<endl;  
}  
};  
class Child:public Parent{  
public:  
Child(int d):Parent(d){//直接调用父类对应构造函数 
cout<<"Child(int)"<<endl;  
}  
Child(){  
cout<<"Child()"<<endl;  
}  
};  
int main()  
{  
Child c1;  
cout<<"----------"<<endl; 
Child c2(5);// 赋值 
}  
解释:从子类中改写来自父类的函数称为覆盖或隐藏 
6.问题:定义父子类,子类对象执行子类函数成员时,子类对象直接调用父类函数成员
思路:声明带实参对象->对象执行父函数成员->对象执行子函数成员->执行析构函数
代码:#include<iostream>  
using namespace std;  
#include<string>  
class Person{  
string name;  
bool gender;  
int age;  
public:  
Person(string n,bool g):name(n),gender(g),age(0){  
cout<<" 做人的准备工作"<<endl;  
}  
void grow(int n){  
age+=n;  
cout<<name<<" 成长到"<<age<<" 岁了"<<endl;  
}  
void show(){  
cout<<" 我是"<<(gender?" 帅哥":"美女")<<name<<",今年"  
<<age<<endl;  
}  
string getName(){return name;}  
bool getGender(){return gender;}  
int getAge(){return age;}  
~Person(){  
cout<<" 做人的退休工作"<<endl;  
}  
};  
class Teacher:public Person{  
string course;  
public:  
Teacher(string n,bool g):Person(n,g){  
cout<<" 做老师的准备工作"<<endl;  
}  
~Teacher(){  
cout<<" 做老师的退休工作"<<endl;  
}  
void setCourse(string c){  
course=c;  
}  
void teach(string classno){  
cout<<getName()<<" 老师在给"<<classno<<"的同学讲"<<course<<" 课程"<<endl;  
}  
void show(){  
cout<<" 我是学校"<<course<<" 老师"<<getName()<<",很高兴认识大家!"<<endl;  
}  
};  
int main()  
{  
Teacher zr(" 赵蓉",false);//在执行子类构造函数中,先执行父类构造函数,用子类实参初始化父类的数据成员
//(等于间接成为父类对象),执行完子类构造函数  
zr.grow(32);//直接执行父类函数成员  
zr.setCourse("Oracle");//执行子类函数成员,实参保存在course变量中  
zr.teach("0805");//执行父类函数成员获得,父类数据成员值name;
//实参获得classno;从course变量获得course  
zr.show();//与上一行同理,获得对应值  
}//父子类的析构按照栈的顺序执行  
解释:1.一个子类的对象也是父类的对象,前提是要在执行子类构造函数时要把子类实参赋值给父类数据成员,进行初始化
2.当class A{public:int x}  
class B:public A{}; 时, 
父类         子类                 其它地方 
直接用x    直接用x       A a;B b;a.x;b.x; 不能直接用x  
7.问题:用例程表示虚函数和多态的概念
思路:1.定义父类Vehicle,子类Car,Bike,Horse,相对独立类Lights
2.声明Car对象,Bike对象,Lights对象。Linght对象运行其函数成员,分两次传送Car对象,Bike对象
3.声明Horse类,Linght对象运行其函数成员,Horse对象
代码:#include<iostream>  
using namespace std;  
#include<string>  
class Vehicle{  
string type;  
public:  
Vehicle(string t):type(t){cout<<"交通工具"<<endl;}  
string getType(){return type;}  
virtual void stop(){cout<<type<<" 停车"<<endl;} //语句体现多态//如果没有virtual关键字,当然会执行“停车”;有,只能执行对应子

类成员函数,而不执行“停车”所在函数体,即相当于空函数体 
}; 
class Car:public Vehicle{  
public:  
void stop(){cout<<" 汽车司机两脚分别踩离合和刹车"<<endl;}  
Car(string t):Vehicle(t){cout<<"汽车"<<endl;}  
};  
class Bike:public Vehicle{  
public:  
void stop(){cout<<" 权哥两手使劲捏车闸"<<endl;}  
Bike(string t):Vehicle(t){cout<<"自行车"<<endl;}  
};  
class Lights{  
public:  
void stopVehicle(Vehicle& v){//注意这是对Vehicle类的引用,即是对父类的引用  
v.stop();  
}  
};  
class Horse:public Vehicle{  
public:  
void stop(){cout<<" 骑士提缰绳吆喝"<<endl;}  
Horse():Vehicle(""){cout<<"马车"<<endl;}//‘’ 参数过渡 
};  
int main()  
{  
Car c(" 小汽车");  
Bike my26("26 车");  
Lights rg;  
rg.stopVehicle(c);  
rg.stopVehicle(my26);  
Horse h;  
rg.stopVehicle(h);  
}  
解释:1.多态:统一管理虚函数。  
如果在调用父类函数时需要执行对象所属的子类中的函数,就需要在父类中把这个函数声明成虚函数 关键字:virtual  
好处:无论以后增加多少子类都不用声明。多态的可拓展性非常强,可以无限扩展,统一管理。
2.一个子类实参,传送到其父类形参之中,先调用父类构造函数,再调用子类构造函数,然后执行函数体(有其父,才有其子)。此函数体结

束时,依次执行父子类析构函数
3.此时,virtual void stop(){cout<<type<<" 停车"<<endl;}可以用下句代替,virtual void stop()=0;//{cout<<type<<" 停车"<<endl;}
。纯虚函数:virtual void stop()=0。  
8.问题:虚函数占用空间是否与其他空函数体函数占用空间相同?
思路:无
代码:#include<iostream>  
using namespace std;  
class A{  
int data;  
char c;  
public:  
void f(){}  
};  
class B{  
int data;  
char c;  
public:  
virtual void f(){}  
};  
int main()  
{  
cout<<"sizeof(A)="<<sizeof(A)<<endl;  
cout<<"sizeof(B)="<<sizeof(B)<<endl;  
}  
解释:有虚函数的可能会有空闲字节
9.问题:程序表达运用指针,引用和动态空间,调用子类函数成员的过程
思路:声明子类对象->声明父类指针,用子类地址初始化,并调用子类函数成员->声明父类引用,用子类地址初始化,并调用子类函数成员->

创建动态数据空间,调用创建类函数成员,释放空间->调用析构函数
代码:#include<iostream>  
using namespace std;  
class Base{  
public:  
void g(){cout<<"Bg"<<endl;}  
virtual void f(){cout<<"Bf"<<endl;}  
//~Base(){cout<<"~Base()"<<endl;} //与下一行有什么区别???为什么少一个~Derived()???
virtual ~Base(){cout<<"~Base()"<<endl;}// 加virtual    
};  
class Derived:public Base{  
public:
void g(){cout<<"Dg"<<endl;}  
virtual void f(){cout<<"Df"<<endl;}  
~Derived(){cout<<"~Derived()"<<endl;}  
};  
int main()  
{  
Derived d;  
Base* p=&d; //指针变量p中内容是对象地址,即父类对象地址 
Base& r=d;//r地址==d地址  
p->f();// (*p).f();  
r.f();//等于d.f();  
cout<<"=================="<<endl;  
p->g();  
r.g();  
cout<<"=================="<<endl;  
p=new Derived;// 创建一个对象,动态存储,不是在栈上 
p->f();// 观察与上一个程序的区别 
delete p;//释放空间,之后就不能调用p了。如果没有virtual,参考解释1.;有,调用子类析构,然调后父类析构,也是多态???  
cout<<"=================="<<endl;  
}  
解释:1.只有在用基类(==父类)指针指向new的派生类(==子类)对象时delete才可能只调基类析构不调派生类析构
2.组合:一个对象“有”几个成员对象 
继承:‘是’,而组合:‘有’; 
多态:统一以父类指针或者引用来管理各种子类对象 
(1)前提:继承关系(2)调用:虚函数(3)??引用或指针 
3.虚函数的使用:(1是规定 2和3是建议) 
1、构造函数不能是虚函数 
2、如果类中有任何一个成员函数是虚函数,那么析构函数应为虚函数 
3、如果一个类肯定被用作其他派生类的基类,尽可能使用虚函数 
10.问题:按照多重继承;同名解析函数;解除同名解析,转化成菱形继承,实现同样功能;这3步思路逐步表示菱形继承
思路1:定义类Phone,MP3,MusicPhone->声明对象mp并初始化,各自调用父类函数成员->执行析构函数
思路2:按照栈的顺序构造和析构及同名解析
思路3:抽象出Product类,形成菱形继承结构
代码1:#include<iostream>  
using namespace std;
#include<string> 
class Phone{  
string mark;  
public:  
Phone(string m):mark(m){  
cout<<" 购"<<m<<" 手机"<<endl;  
}  
void call(string name){  
cout<<" 用"<<mark<<"手机给"<<name<<" 拨电话"<<endl;  
}  
~Phone(){  
cout<<" 手机报废"<<endl;  
}  
};  
class MP3{  
public:  
MP3(int n){  
cout<<n<<"G 的MP3"<<endl;  
}  
void play(string song){  
cout<<" 播放歌曲"<<song<<endl;  
}
~MP3(){  
cout<<" MP3报废"<<endl;  
}    
};  
class MusicPhone:public Phone,public MP3{//多重继承  
public:  
MusicPhone(string m):Phone(m),MP3(4){  
cout<<"......."<<endl;  
}  
};  
int main()  
{  
MusicPhone mp(" 夏新");  
mp.call(" 芙蓉");  
mp.play(" 你是我的玫瑰花");  
}  
代码2:
#include <iostream>  
using namespace std;
#include<string> 
class Phone{  
double price;  
string mark;  
public:  
Phone(string m, double p):mark(m),price(p){//修改部分  
cout << " 购" << m << " 手机" << endl;  
}  
void call(string name){  
cout << " 用" << mark << " 手机给" << name << " 拨电话" << endl;  
}  
double getPrice(){return price;} //添加部分 
~Phone(){  
cout << " 手机报废" << endl;  
}  
};  
class MP3{  
double price;  
public:  
MP3(int n, double p):price(p){ //修改部分 
cout << n << "G 的MP3" << endl;  
}  
void play(string song){  
cout << " 播放歌曲" << song << endl;  
}  
double getPrice(){return price;} //添加部分 
~MP3(){  
cout<<" MP3报废"<<endl;  
}
};  
class MusicPhone: public Phone, public MP3{  
public:  
MusicPhone(string m, double p):Phone(m,p),MP3(4,p){  
cout << "...." << endl;  
}  
};
int main()  
{  
MusicPhone mp(" 夏新", 1234.5);  
mp.call(" 芙蓉");  
mp.play(" 你是我的玫瑰花");  
cout<<mp.Phone::getPrice()<<endl;// 指定getPrice,因为有两个;多重继承中的同名解析 
}    
代码3:#include <iostream>  
using namespace std;  
#include<string>
class Product{//增加部分  
double price;  
public:  
Product(double p):price(p){  
 cout << "price " << p << endl;  
}   
 double getPrice(){return price;}//想得到类中私有数据成员的值,只能通过调用公开函数成员来实现。  
};   
class Phone : virtual public Product{//修改部分  
string mark;  
public:  
Phone(string m, double p):mark(m),Product(p){  
cout << " 购" << m << " 手机" << endl;  
}  
void call(string name){  
cout << " 用" << mark << " 手机给" << name << " 拨电话" << endl;  
}  
~Phone(){  
cout << " 手机报废" << endl;  
}  
};  
class MP3: virtual public Product{//修改部分  
public:  
MP3(int n, double p):Product(p){  
cout << n << "G 的MP3" << endl;  
}  
void play(string song){  
cout << " 播放歌曲" << song << endl;  
}  
~MP3(){  
cout<<" MP3报废"<<endl;  

};  
class MusicPhone: public Phone, public MP3{  
public:  
MusicPhone(string m, double p):Phone(m,p),MP3(4,p),Product(p){  
cout << "...." << endl;  
}  
};  
int main()  
{  
MusicPhone mp(" 夏新", 1234.5);  
mp.call(" 芙蓉");  
mp.play(" 你是我的玫瑰花");  
cout << mp.getPrice() << endl; //不同部分 
}  
解释:虚继承:表示来自父类的成员允许合并 
虚基类 菱形继承 虚基类的构造函数由底层子类直接传递
11.问题:How把一个常量转换成变量 
思路:无
代码:#include<iostream>  
using namespace std;  
int main()  
{  
int n=100;  
const int m=n;//这时的m是常量,但是不是固定值  
cout<<"m="<<m<<endl;  
//m=200; //如果试图改变其值,编译将会出错 
cout<<"----------"<<endl; 
const_cast<int&>(m)=300;//通过类型转换改变其值  
cout<<"m="<<m<<endl;  
const int c=400; //常量固定值 
const_cast<int&>(c)=500; //不能改变其值??? 
cout<<"c="<<c<<endl;//maybe 400  
const int* p=&c; //事实上,其值已变 
cout<<"c="<<*p<<endl;  
}  
解释:类型转换的格式:xxx—cast< 类型> ( 数据)  
类型转换 (类型)数据 / 类型(数据) / (类型)(数据) 
类型转换 算子 尽量不用类型转换 
static—cast< 类型>(数据) 用于合理的类型转换  
(1) 数值类型之间(2) 具体指针与void* 之间(3) 父子类之间及其 
指针间(4) 无名/临时对象形式的 
不合理的:const—cast<T&>(T 类型常量)= 值;改变一个常量的值 
12.问题:父子类动态类型转换例程
思路:定义父类指针动态空间,一个指向父类空间,一个指向子类空间;定义两个子类指针,把父类和子类指针转换类型,使子类指针指向它

们;输出结果
代码:#include<iostream>  
using namespace std;  
class Person{  
public:virtual void work(){}  
};  
class Driver:public Person{  
public:  
virtual void work(){}// 开车 
};  
int main()  
{  
Person* p1=new Person;  
Person* p2=new Driver;  
Driver* d1=dynamic_cast<Driver*>(p1);  
Driver* d2=dynamic_cast<Driver*>(p2);  
cout<<"d1="<<d1<<endl;  
cout<<"d2="<<d2<<endl;  
}  
解释:1.dynamic_cast<子类*>(父类指针)  
从父类指针到子类指针的尝试转换,如果是子类类型转换成功将有其地址,不成功就会转换成NULL,也就是空指针,如d1=0。 
多态中要求父类中必须有虚函数 
尽量只使用多态,而不是作类型转换和类型识别
2.reinterpret_cast<>()  
1/任意两种指针之间 
2/指针与足够宽度的数值类型之间 
避免用它 
13.问题:用1/2+1/3=5/6,解释友元授权
思路:友元授权(友元,即特殊关系户,当然受到与父子关系同等待遇,汗颜!)
代码:#include<iostream>  
using namespace std;  
class F{  
int n;  
int d;  
public:  
F(int n,int d):n(n),d(d){}  
friend F add(const F& f1,const F& f2);// 形参的名字可以不写// 授权函数,功效等同于继承 
friend ostream& output(ostream& os,const F& f);// 授权函数,功效等同于继承 
};  
F add(const F& f1,const F& f2)// 养成带const 的习惯 
{  
int d=f1.d*f2.d;// 必须是对象.fd  
int n=f1.n*f2.d+f1.d*f2.n;  
return F(n,d);  
}  
ostream& output(ostream& os,const F& f)//os 相当于cout  
{//将void 该成了ostream ( 注意) 
os<<f.n<<'/'<<f.d;  
return os;  
}  
int main()  
{  
F f1(1,2),f2(1,3);  
output(cout,f1)<<"+";  
output(cout,f2)<<"=";  
output(cout,add(f1,f2))<<endl;  
}  
解释:友元关键字:friend 不是成员。主要用在运算法重载中,类对某函数授权,允许直接访问本类对象中的任何成员,必须在本类内部授权

不是本类成员的函数
14.问题:友元类例程
思路:简直就是结义金兰!想用什么用什么!
1.看主函数:声明三个父类对象(想:就联想到父类的成员:数据和函数,那就顺便看一下)->声明友元类对象(想:这个对象即可以用自身

类成员,也能用父类成员,顺便看一下)->对象用自身函数成员,实参为父类对象及对于数据成员值,通过引用存到指定对象的指定数据成员

中->输出父类对象数据成员->对象自身函数成员,实参为两个父类对象(调用指定对象指定数据成员值)->对象自身函数成员,实参为父类对象

和参加运算参数->输出父类对象数据成员->同理计算输出距离
2.(原来还想进一步细致深入的研究一下深层运行,看主函数,捎带整个程序运行完毕)
代码:#include<iostream>  
using namespace std;  
#include<cmath>  
class Point{  
double x;  
double y;  
friend class Util;// 把整个类都设为友元 
};  
class Util{  
public:  
void move(Point& p,double x,double y){//关系真铁,太霸道了  
p.x+=x;  
p.y+=y;  
}  
double distance(const Point& p1,const Point& p2){  
double x=p1.x-p2.x;  
double y=p1.y-p2.y;  
return sqrt(x*x+y*y);  
}  
void set(Point& p,double x,double y){//关系真铁,太霸道了//引用竟然可以这样用?原来是对象的引用啊!
//为什么这样赋值?不用构造函数?尚未比较清楚,且看以后2遍更新!  
p.x=x;  
p.y=y;  
}  
void print(const Point& p){  
cout<<'('<<p.x<<','<<p.y<<')'<<endl;  
}  
};  
int main()  
{  
Point p1,p2,p3;  
Util u;  
u.set(p1,0,0);  
u.set(p2,3,0);  
u.set(p3,3,4);  
u.print(p1);  
u.print(p3);  
cout<<u.distance(p1,p3)<<endl;  
u.move(p2,12,-1);  
u.print(p2);  
cout<<u.distance(p2,p3)<<endl;  
}  
解释:得到结果不重要,重在整个程序执行过程,多在脑中转几遍,过程中的几个概念要记住。 
15.问题:只读函数成员例程
思路:无
代码:#include<iostream>  
using namespace std;  
#include<string>  
class Course{  
string name;  
int days;  
public:  
Course(string n,int d):name(n),days(d){}  
void show()const{//const 表示本函数不会修改当前对象 
cout<<name<<" 课程"<<days<<"天"<<endl;(注意*)  
}  
void show(){// 注意没有const//若注释掉本函数,将得不同结果  
cout<<name<<"* 课程"<<days<<"天"<<endl;(注意*)  
}  
void extend(int d){  
days+=d;  
}  
};  
int main()  
{  
Course cpp("C++",19);  
Course uni("UNIX",3);// 不能用unix  
cpp.extend(1);  
cpp.show();  
uni.show();  
}  
解释:静态和只读成员,只读函数(常量函数)。有const函数,肯定不改;无const函数,有可能改。
16.问题:初始化数组对象,定义初始化调用静态数据成员。给出例程
思路:声明数组对象(包括初始化),循环调用对象函数成员(执行时会调用静态数据成员值)
代码:#include <iostream>  
using namespace std;  
#include <string>  
class Student{  
string name;  
bool gender;  
public:  
static int room;//静态数据成员  
static string teacher;//静态数据成员  
Student(string n, bool g=true):name(n),gender(g){  
cout << (gender?" 帅哥":"美女") << name << " 报名了" << endl;  
}  
void study(){  
cout << name << " 在" << room << " 听" << teacher << " 讲课" << endl;  
}  
static void changeRoom(int r){//静态函数成员,未用  
room = r;  
}  
static void changeTeacher(string t){//静态函数成员  
teacher = t;  
}  
};  
int Student::room = 411;//静态数据成员的初始化  
string Student::teacher = " 赵蓉";//静态数据成员的初始化  
int main()  
{  
Student s[4]={  
Student(" 杨智"),  
Student(" 史闻军"),  
Student(" 廖英旭",false),  
Student(" 周桐"),    
};  
for(int i=0; i<4; i++)  
s[i].study();  
Student::changeTeacher(" 大哥");//改变静态数据成员的值  
for(int i=0; i<4; i++)  
s[i].study();  
}  
解释:不依赖于对象只依赖于类,静态数据成员的初始化要在外面进行。在静态函数成员中没有当前对象,只能访问不依赖于对象的成员,也
就是静态成员。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值