今天给团队的新成员上了一下面向对象思想的课,贴一下课件(可能有一些语法上的错误,请见谅)。
封装
我们以square代表”正方形”这种类,正方形有color,正方形可以display。这里color就是成员变量,display是成员变量。
这个类我们可以这么写:
class CSquare
{
private:
int m_color;
public:
void display(){…}
void setcolor(int color){ m_color = color}
void getcolor(){return m_color}
}
Csquare square = new Csquare();
square.m_color=blue;
square.display();
成员变量可以只在类内部处理,也可以开放给外部处理。以数据封装的的目的而言,前者较为妥当。
把数据声明为private,不允许外界随意存取,只通过特定接口来操作,这就是封装。
继承
考虑以下几个类
class CShape //形状
{
private:
int m_color;
public:
void setcolor(int color){ m_color = color}
};
class CRect : public CShape //矩形是一种形状
{
public:
void display(){…}
};
class CEllipse:public CShape //椭圆是一种形状
{
public:
void display(){…}
};
class CTriangle:public CShape //三角形是一种形状
{
public:
void display(){…}
};
this指针
事实:
现在有两个CRect的实例对象rect1 , rect2他们有各自的m_color,但他们共享父类CRect::setcolor(其实是CShape::setcolor).
也就是说:
rect1.setcolor(color) 调用的是 CRect::setcolor(color)
rect2.setcolor(color) 调用的是 CRect::setcolor(color)
问题来了:
同一个setcolor他怎么知道要把color赋值给rect1的m_color还是rect2的m_color
真相:
调用的是CRect::setcolor(int color,CRect* this){ this.m_color = color }
多态
问题:
以上三个类都有display方法,可不可以提升到CShape上去,然后再继承?
答案:能?不能?
需求:
我们想用一个循环进行形状打印
Cshape shape=new Cshape();
Crect rect;
rect = shape;//对不对?
CShape shapes[3];
shapes[0] = new CRect();//为什么可以这样赋值?
shapes[1] = new CTriangle ();
shapes[2] = new CEllipse();
for (int i=0; i<3;i++)
{
shapes[i].display();//会不会报错?
}
思考:以上所做的工作能实现这个需求吗,打印出来是想要的结果吗?
怎么实现?
virtual(虚函数)
改写CShape类:
class CShape //形状
{
private:
int m_color;
public:
void setcolor(int color){ m_color = color}
virtual void display(){}
virtual void test(){}//纯粹为了说明虚函数而加上去的
};
对象实例内存形态:
vtable
vptr | ---------à | (*display)() | ------------à | CShape::display() |
m_color |
| (*test)() | ------------à | CShape::test() |
改写CRect类:
class CRect : public CShape //矩形是一种形状
{
public:
virtual void display(){打印矩形}
};
vptr | ---------à | (*display)() | ------------à | CRect::display() |
m_color |
| (*test)() | ------------à | CShape::test() |
1、当调用一个对象的函数时,系统会直接去检查这个对象声明定义的类,即声明类,看所调用的函数是否为虚函数;
2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。
3、在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚函数的父类为止,然后执行该父类里重载后的函数。
using System;
namespace Zhisi.Net
{
class A
{
public virtual void Func() // 注意virtual,表明这是一个虚拟函数
{
Console.WriteLine("Func In A");
}
}
class B : A // 注意B是从A类继承,所以A是父类,B是子类
{
public override void Func() // 注意override ,表明重新实现了虚函数
{
Console.WriteLine("Func In B");
}
}
class C : B // 注意C是从A类继承,所以B是父类,C是子类
{
}
class D : A // 注意B是从A类继承,所以A是父类,D是子类
{
public new void Func() // 注意new ,表明覆盖父类里的同名类,而不是实现
{
Console.WriteLine("Func In B");
}
}
class program
{
static void Main()
{
A a; // 定义一个a这个A类的对象.这个A就是a的声明类
A b; // 定义一个b这个A类的对象.这个A就是b的声明类
A c; // 定义一个c这个A类的对象.这个A就是b的声明类
A d; // 定义一个d这个A类的对象.这个A就是b的声明类
a = new A(); // 实例化a对象,A是a的实例类
b = new B(); // 实例化b对象,B是b的实例类
c = new C(); // 实例化b对象,C是b的实例类
d = new D(); // 实例化b对象,D是b的实例类
a.Func(); // 执行a.Func:1.先检查声明类A 2.检查到是虚拟方法 3.转去检查实例类A,就为本身 4.执行实例类A中的方法 5.输出结果 Func In A
b.Func(); // 执行b.Func:1.先检查声明类A 2.检查到是虚拟方法 3.转去检查实例类B,有重载的 4.执行实例类B中的方法 5.输出结果 Func In B
c.Func(); // 执行c.Func:1.先检查声明类A 2.检查到是虚拟方法 3.转去检查实例类C,无重载的 4.转去检查类C的父类B,有重载的 5.执行父类B中的Func方法 5.输出结果 Func In B
d.Func(); // 执行d.Func:1.先检查声明类A 2.检查到是虚拟方法 3.转去检查实例类D,无重载的(这个地方要注意了,虽然D里有实现Func(),但没有使用override关键字,所以不会被认为是重载) 4.转去检查类D的父类A,就为本身 5.执行父类A中的Func方法 5.输出结果 Func In A
D d1 = new D();
d1.Func(); // 执行D类里的Func(),输出结果 Func In D
Console.ReadLine();
}
}
}
委托
this.button_daoshu.Click += new System.EventHandler(this.fuhao_Click);
private void fuhao_Click(object sender, EventArgs e)
概念:
什么是委托:
1. 类型安全的函数指针
2. 方法特征的一个名称
什么是事件:
1. widows消息的封装
2. 委托的一种特殊形式
public delegate void CatShoutEventHandler();
pubic event CatShoutEventHandler CatShout;
class cat
{
private string name;
public Cat(string name)
{
this.name = name;
}
public delegate void CatShoutEventHandler();
public event CatShoutEventhandler CatShout;
public void Shout()
{
console.writeline(“喵,我是{0}.”,name);
if(CatShout != null)
{
CatShout();//注意无参数无返回值
}
}
class Mouse
{
private string name;
public Mouse(string name)
{
this.name = name;
}
public void Run()
{
console.writeline(“老猫来了,{0}快跑!”,name);
}
}
static void Main(String[] args)
{
Cat cat = new Cat(“Tom”);
Mouse mouse1=new Mouse(“Jerry”);
Mouse mouse2=new Mouse(“Jack”);
//多播委托
cat.CatShout += new Cat.CatShoutEventHandler(mouse1.Run);
cat.CatShout += new Cat.CatShoutEventHandler(mouse2.Run);
cat.Shout();
Console.Read();
}
输出结果:
喵,我是Tom
老猫来了,Jerry快跑!
老猫来了,Jack快跑!
问题:
private void button1_Click(object sender,EventArgs e)
{
…
}
sender是什么?e又是什么?
添加如下类:
public class CatShoutEventArgs:EventArgs
{
private string name;
public string name
{
get{return name;}
set{name = value}
}
}
改写cat
class cat
{
private string name;
public Cat(string name)
{
this.name = name;
}
public delegate void CatShoutEventHandler(object sender, CatShoutEventArgs args);
public event CatShoutEventhandler CatShout;
public void Shout()
{
console.writeline(“喵,我是{0}.”,name);
if(CatShout != null)
{
CatShoutEventArgs e = new CatShoutEventArgs();
e.Name = this.name;
CatShout(this , e);//必须有参数
}
}
改写Mouse
class Mouse
{
private string name;
public Mouse(string name)
{
this.name = name;
}
public void Run(object sender, CatShoutEventArgs args)
{
console.writeline(“老猫{0}来了,{1}快跑!”,args.Name, name);
}
}
Main函数不变
输出结果:
喵,我是Tom
老猫Tom来了,Jerry快跑!
老猫Tom来了,Jack快跑!