首页 服务器 编程 必备知识 搜索引擎 圩日手册
站内搜索
最近浏览
推荐文章
热文排行

『21天精通C++』第三周学习笔记


第十五天和第十六天

  从已存在的类继承得到新类的过程叫做派生。派生是继承的过程。
用:将派生类和基类分开。派生类将继承基类所有公有成员。(构造函数和析构函数除外)
不要设计有许多私有成员的基类。

protected:被保护成员,可以被基类和派生类使用。通常基类只包括公有和被保护成员。
如果希望类外程序包括派生类都无权访问类中数据,那么将其定义为私有。

class base:private emp//取代继承时VC++提供的缺省设置方式是private.即是从基类继承到的成员在派生类全都是私有成员。

class base:protected emp//继承来的保护成员和公有成员在派生类中全部都为被保护成员。

最常用的是这种:
class base:public emp//基类中的保护成员在派生类仍旧为保护成员,基类中的公有成员在派生类中仍旧为公有成员。

  内部的缺省构造函数没有处理常量的能力。因此要求类中出现常量成员时需要自定义构造函数。并且需要利用构造初始化表,构造函数才能真正的进行构造和初始化,而不是构造和赋值。

class c{
int i;
char a;
float b;
public:
c(int i,char a,float b)://此处时冒号,构造函数原型声明,可以和初始化表在一行。
i(I),a(A),b(B){};//此处是初始化表
}

注意:字符数组成员和指针通常被保存在堆中,故要求在函数内赋值。
类中没有数组成员时,应当用构造初始化表完成所有初始化工作。

为什么要用继承:

  通过继承再次利用了自己编写的代码,既维持了对数据的保护,又是开发程序的有利工具。如果买来了类工具库,甚至可以在没有实现部分源代码的情况下派生出新的类来,当需要得到一个和类库所提供的不同的窗口类时,可通过继承得到一个略有不同的新窗口。

  继承的一个优点是可以在已经了解的编写好的代码基础上编写新的代码,从已经编写并调试好的类中很容易就能够派生出新的类。通过继承复用代码和数据,比非OOP程序设计中的复用更加安全有效。

被保护的访问权限用在何处?

  在VC++添加被保护的访问权限是为了在维持对数据保护的情况下完成继承。派生类不能使用基类中的私有成员。
  将基类中的某些成员定义为保护成员防止了类外的程序对其的访问,同时又允许派生类访问,被保护的成员仍旧不被余下程序所见,但所有的派生类都可以访问它。如果没有被保护访问权限就不能做到既对数据进行保护(私有性)又可以使派生类可以访问。
由一个基类得到派生类可以做那些改动。
  可以在派生类添加数据成员和功能函数。子类总是比父类功能强大。

为什么单向继承优于多向继承?

  单向继承代码比多向继承代码易于编写,可以加快编程速度并可减少错误。

为什么派生类必须定义访问权限?

  因为在VC++中所有的派生类继承基类时缺省为私有继承。这种限制会带来不便。
  将访问权限定义为protected将使得继承得到的公有成员和保护成员在派生类中都是保护成员。
  将访问权限定义为public将使得继承得到的公有成员仍为公有成员,保护成员仍为保护成员,这是最通用的方式。

  用构造初始化表后,除了能构造常量成员对象外,还可以使构造函数变的简捷,构造初始化表负责构造派生类。
  字符数组必须在函数体内被赋值。字符数组成员和指针成员通常被保存在堆中故而要求在构造函数体内赋值。

例子程序:

#include
#include
class Parent{
protected:
char name[25];
int age;
public:
Parent(char[],int);
~Parent(){};
void disparent(void);
};
Parent::Parent(char N[],int A):age(A){
strcpy(name,N);
}
void Parent::disparent (void){
cout<<"Parent''s name is:"< cout<<"Parent''s age is:"< }
class Son:Parent{
int yrInSchool;
public:
void dispSon(void);
Son(char[],int,int);
};
Son::Son(char N[],int A,int Y):Parent(N,A),yrInSchool(Y){
}
void Son::dispSon (void){
cout<<"Son''s name is:"< cout<<"Son''s age is:"< cout<<"Son year IN school is:"< }
class Daughter:Parent{
int yrInSchool;
char friendsName[25];
public:
void dispDaughter(void);
Daughter(char[],int,int,char[]);
};
Daughter::Daughter(char N[],int A,int Y,char F[]):Parent(N,A),yrInSchool(Y)
{
strcpy(friendsName,F);
}
void Daughter::dispDaughter (void){
cout<<"Daughter''s name is:"< cout<<"Daughter''s age is:"< cout<<"Daughter year IN school is:"< cout<<"Daughter''s FriendsName is:"< };

main(){
Parent mom("Betty",58);
Parent dad("tom",60);
mom.disparent ();
dad.disparent ();
Son boy("smalltom",17,11);
boy.dispSon ();
Daughter girl("ketey",18,12,"june");
girl.dispDaughter ();
return 0;
}

  子类构造基类时,初始化表的参数顺序和父类的初始化表一致。这样才能将正确的值传递给父类的构造函数。
  先构造基类对象在构造派生类对象。
  如果需要限制或改变继承到成员的功能,在派生类中定义同名的数据成员或成员函数。
  VC++自动析构继承对象从最低的派生类到基类。

第十七天:

当一个类包含其他类时用合成。
当一个类是另一个类的扩展,它包含另一个类的一些属性,另外又增加了一些属性行为用继承。

一个由其他对象合成的对象在它的所有成员对象初始化之前不能被初始化。
构造合成类本身之前应该首先用构造函数初始化表构造成员类。
提供构造整个合成类所需的所有参数,包括构造类中的每个成员类的所需参数。
不需调用成员类的析构函数。
不能访问成员类的私有数据。要想访问它们必须通过调用公有成员函数。

当一个类和另一个类只有个别成员函数或数据成员不同时使用继承的方法。当一个类包含另一个或更多的类时使用合成。但合成类并不仅仅是其他类的功能扩展版本。对于继承构造函数初始化表在建立对象时扮演了重要的角色,当构造一个合成类对象时,则必须肯定所有成员元件都已被构造了。
如果合成类或某个成员元件类包含指针成员,就必须编写重载赋值函数。


十八天:

  在大多数程序中不使用虚函数,就使用静态联编。除非使用了函数指针或是虚函数,否则C/VC++编译器都采用静态联编方式。
  静态联编是指在编译时而非运行时确定函数调用。此方法效率高。

  动态联编是指在运行时才能确定函数调用。OOP中需要进行动态联编的场合是使用一个指向类家族所有类的指针。只要这个指针指向基类,就能指向任何一个派生类,编译器无法确定指针究竟指向哪个类,直到运行时才能确定。加入Virtual关键字是为了告诉编译器等到运行时在确定哪个函数被调用。基类的还是派生类的函数。

  使用Virtual要求动态联编时,VC++在函数调用处插入一个间接指针,不是指向函数首址,而是指向虚拟函数调用地址表(指向函数首址是直接指针)这个表叫做VTABLE(Virtual Table 虚拟表)。在运行时,对象指针触发成员函数在虚拟表中的偏移量调用正确的函数。
  纯虚函数不包括任何代码,可以选择给纯虚函数赋0值,从而使编译器不能让用户试图实例化一个包含纯虚函数的基类。

  任何包含一个或多个纯虚函数的基类称做抽象基类。不能实例化,成员函数是空的,但可以用它作为它的派生类层次的模式,这样一个基类提供一个所有类家族中类的框架。抽象基类可以列出所有公用数据成员和成员函数,以便派生类继承。

file://Filename: 1206.CPP
#include
#include
#include
#include
int dispMenu(void);
void addParts(void);
void prParts(void);
class inventoryItem{
char partCode[5];
char descrip[20];
int num;
float price;
public:
void addToInv(char P[],char D[],int N,float PR)
{
num=N;
price=PR;
strcpy(partCode,P);
strcpy(descrip,D);
this->toDisk();
}
void toDisk(void)
{
ofstream invOut;
invOut.open ("INV.DAT",ios::app);
invOut.write ((char *)this,sizeof(*this));
}
void getData(void);
};
void inventoryItem::getData (void)
{
ifstream invIn("INV.DAT");
while(invIn)
{
invIn.read((char *)this,sizeof(*this));
if(invIn.good())
{
cout< cout< cout<<"\npart code:"< cout<<"description:"< cout<<"Quantitly:"< cout<<"Price:"< }
}
}
main()
{
int menu;
do
{
menu=dispMenu();
switch(menu)
{
case 1:addParts();
break;
case 2:prParts();
break;
case 3:exit(1);
default:
cerr<<"\n* * * Enter 1,2or3 * * *\n";
}
}while(menu!=3);
return 0;
}
int dispMenu(void)
{
int ans;
cout<<"\n\n功能菜单:\n\n";
cout<<"1.增加记录\n";
cout<<"2.显示记录\n";
cout<<"3.退出\n";
cin>>ans;
return ans;
}
void addParts(void)
{
char pc[5];
char de[20];
int q;
float pr;
inventoryItem part;
cin.ignore ();
cout<<"名称:";
cin.getline(pc,5);
cout<<"说明:";
cin.getline (de,20);
cout<<"数量:";
cin>>q;
cout<<"价格:";
cin>>pr;
part.addToInv (pc,de,q,pr);
}
void prParts(void)
{
inventoryItem part;
part.getData ();
}
 
  『21天精通C++』自学笔记到此就结束了,因是一年多以前的东西了所以不是很完整,当时学的时候只记得虽然书名是21天可雷神共读了三遍用了半年的时间,自以为还没有入门?好象明白了但又什么也不能做,这是怎么回事?雷神一个劲的 @#%$%$@!@@#~¥#。不过以我的性格既然开始了就不放弃,继续学下去,我看的下一本书是『VC技术内幕』。

[wangjy17908]
添加时间:2008-01-05
版权所有(C)2005-2015