面向对象 -教案

面向对象思想:
万物皆对象


面向对象三大特性:
封装:
        封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就    是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。

    简介

    
    在面向对象编程中,封装(encapsulation)是将对象运行所需的资源封装在程序对象中——基本上,是方法和数据。对象是“公布其接口”。其他附加到这些接口上的对象不需要    关心对象实现的方法即可使用这个对象。这个概念就是“不要告诉我你是怎么做的,只要做就可以了。”对象可以看作是一个自我包含的原子。对象接口包括了公共的方法和初始化    数据。

简介


在面向对象编程中,封装(encapsulation)是将对象运行所需的资源封装在程序对象中——基本上,是方法和数据。对象是“公布其接口”。其他附加到这些接口上的对象不需要关心对象实现的方法即可使用这个对象。这个概念就是“不要告诉我你是怎么做的,只要做就可以了。”对象可以看作是一个自我包含的原子。对象接口包括了公共的方法和初始化数据。

程序


封装 (encapsulation)
隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。
封装途径
封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。
封装在网络编程里面的意思, 当应用程序用TCP传送数据时,数据被送入协议栈中,然后逐个通过每一层直到被当作一串比特流送入网络,其中每一层对收到的数据都要增加一些首部。
 

原则


1把尽可能多的东西藏起来.对外提供简捷的接口。
2把所有的属性藏起来。
例如,在抽象的基础上,我们可以将时钟的数据和功能封装起来,构成一个时钟类。
c++的语法,时钟类的声明如下:
class Clock
{
public: //共有成员,外部接口
void SetTime(int NewH,int NewM,int NewS);
void ShowTime();
private: //私有成员,外部无法访问
int Hour,Minute,Second;
}
 

技巧


可以看到通过封装使一部分成员充当类与外部的接口,而将其他的成员隐蔽起来,这样就达到了对成员访问权限的合理控制,使不同类之间的相互影响减少到最低限度,进而增强数据的安全性和简化程序的编写工作。

 

 

继承:

         通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类    为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。

定义

在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承
派生类的定义格式
单继承的定义格式如下:
1
2
3
4
class<派生类名>:<继承方式><基类名>
{
<派生类新定义成员>
};
其中,class是关键词,<派生类名>是新定义的一个类的名字,它是从<基类名>中派生的,并且按指定的<继承方式>派生的。<继承方式>常使用如下三种关键字给予表示:
public 表示公有继承;
private 表示私有继承;
protected 表示保护继承;
多继承的定义格式如下:
1
2
3
4
5
6
7
class<派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
 
{
 
<派生类新定义成员>
 
};
可见,多继承与单继承的区别从定义格式上看,主要是多继承的基类多于一个。
如果省略继承方式,对'class'将采用私有继承,对'struct'将采用公有继承。
也就是说
1
2
3
4
5
class Base1{};
 
struct Base2{};
 
class Derive:Base1,Base2{};
那么,Derive类将私有继承Base1,公有继承Base2。相当于:
1
class Derive:private Base1,public Base2{};
 

继承方式


公有继承(public)、私有继承(private)、保护继承(protected)是常用的三种继承方式。
1. 公有继承(public)
公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。
2. 私有继承(private)
私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
3. 保护继承(protected)
保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。
下面列出三种不同的继承方式的基类特性和派生类特性。
 
public
protected
private
公有继承
public
protected
不可见
私有继承
private
private
不可见
保护继承
protected
protected
不可见
为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。
 

公有方式

(1) 基类成员对其对象的可见性:
公有成员可见,其他不可见。这里保护成员同于私有成员。
(2) 基类成员对派生类的可见性:
公有成员和保护成员可见,而私有成员不可见。这里保护成员同于公有成员。
(3) 基类成员对派生类对象的可见性:
公有成员可见,其他成员不可见。
所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。
 

私有方式

(1) 基类成员对其对象的可见性:
公有成员可见,其他成员不可见。
(2) 基类成员对派生类的可见性:
公有成员和保护成员是可见的,而私有成员是不可见的。
(3) 基类成员对派生类对象的可见性:
所有成员都是不可见的。
所以,在私有继承时,基类的成员只能由派生类中的成员函数访问,而且无法再往下继承。
 

保护方式

这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,对基类成员有不同的可见性。
上述所说的可见性也就是可访问性。关于可访问性还有另的一种说法。这种规则中,称派生类的对象对基类访问为水平访问,称派生类的派生类对基类的访问为垂直访问。
 

一般规则

编辑
公有继承时,水平访问和垂直访问对基类中的公有成员不受限制;
私有继承时,水平访问和垂直访问对基类中的公有成员也不能访问;
保护继承时,对于垂直访问同于公有继承,对于水平访问同于私有继承。
对于基类中的私有成员,只能被基类中的成员函数和友元函数所访问,不能被其他的函数访问。
基类与派生类的关系
任何一个类都可以派生出一个新类,派生类也可以再派生出新类,因此,基类和派生类是相对而言的。
 

两类关系


 

具体化

类的层次通常反映了客观世界中某种真实的模型。在这种情况下,不难看出:基类是对若干个派生类的抽象,而派生类是基类的具体化。基类抽取了它的派生类的公共特征,而派生类通过增加行为将抽象类变为某种有用的类型。
 

延续化

先定义一个抽象基类,该基类中有些操作并未实现。然后定义非抽象的派生类,实现抽象基类中定义的操作。例如,虚函数就属此类情况。这时,派生类是抽象的基类的实现,即可看成是基类定义的延续。这也是派生类的一种常用方法。
 

派生类

多继承时,一个派生类有多于一个的基类,这时派生类将是所有基类行为的组合。
派生类将其本身与基类区别开来的方法是添加数据成员和成员函数。因此,继承的机制将使得在创建新类时,只需说明新类与已有类的区别,从而大量原有的程序代码都可以复用,所以有人称类是“可复用的软件构件”。
 

成员调整


控制方式

访问声明采用作用域"::" ,它的一般形式为:基类名::成员名;。在派生类的类界面中,将这些访问声明放在合适的访问控制保留字之后,从而改变在派生类中该成员的访问控制方式。
 

重定义

如果在派生类中定义了一个函数原型与继承成员函数一模一样的成员函数,则该函数实现的函数体是对继承成员函数的重定义。


多态:

多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数实现的。

形式


多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
C++中,实现多态有以下方法:虚函数抽象类,覆盖,模板(重载和多态无关)。
OC中的多态:不同对象对同一消息的不同响应.子类可以重写父类的方法
多态就是允许方法重名 参数或返回值可以是父类型传入或返回。
多态也指生物学中腔肠动物的特殊的生活方式。水螅态与水母态的世代交替现象。
 

作用


把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。
举个例子:从一个基类中派生,响应一个虚命令,产生不同的结果。
比如从某个基类继承出多个对象,其基类有一个虚方法Tdoit,然后其子类也有这个方法,但行为不同,然后这些子对象中的任何一个可以赋给其基类对象的引用,或者将子对象地址赋给基类指针,这样其基类的对象就可以执行不同的操作了。实际上你是在通过其基类来访问其子对象的,你要做的就是一个赋值操作。
使用继承性的结果就是可以创建一个类的家族,在认识这个类的家族时,就是把导出类的对象当作基类的对象,这种认识又叫作upcasting。这样认识的重要性在于:我们可以只针对基类写出一段程序,但它可以适应于这个类的家族,因为编译器会自动找出合适的对象来执行操作。这种现象又称为多态性。而实现多态性的手段又叫称动态绑定(dynamic binding)。
简单的说,建立一个父类的对象,它的内容可以是这个父类的,也可以是它的子类的,当子类拥有和父类同样的函数,当使用这个对象调用这个函数的时候,定义这个对象的类(也就是父类)里的同名函数将被调用,当在父类里的这个函数前加virtual关键字,那么子类的同名函数将被调用。通俗点说就是父类不加virtual关键字,那么子类的同名函数将会被覆盖。OC中当一个父类对象指针指向的是一个子类对象时,调用方法时,会执行直接子类的方法。
 

例子


在C++中:
classA
{
public:
    A(){}
    virtual void foo()
    {
        cout<<"ThisisA."<<endl;
    }
};

classB:publicA
{
public:
    B(){}
    void foo()
    {
        cout<<"ThisisB."<<endl;
    }
};

int main(intargc,char*argv[])
{
    A* a = new B();
    a->foo();
    if(a != NULL)
    delete a;
    return0;
}
这将显示:
This is B.
如果把virtual去掉,将显示:
This is A.
前面的多态通过使用虚函数virtual void foo()来实现。
在java中:
多态,是面向对象的程序设计语言最核心的特征。多态,意味着一个对象有着多重特征,可以在特定的情况下,表现不同的状态,从而对应着不同的属性和方法。从程序设计的角度而言,多态可以这样来实现(以java语言为例):
public interface Parent//父类接口
{
    public void simpleCall();
}
public class Child_A implements Parent
{
    public void simpleCall();
    {
    //具体的实现细节;
    }
}

public class Child_B implements Parent
{
    public void simpleCall();
    {
    //具体的实现细节;
    }
}
//当然还可以有其他的实现
然后,我们就可以看到多态所展示的特性了:
Parent pa = new Child_A();
pa.simpleCall()则显然是调用Child_A的方法;
Parent pa = new Child_B();
pa.simpleCall()则是在调用Child_B的方法。所以,我们对于抽象的父类或者接口给出了我们的具体实现后,pa 可以完全不用管实现的细节,只访问我们定义的方法,就可以了。事实上,这就是多态所起的作用,可以实现控制反转这在大量的J2EE轻量级框架中被用到,比如Spring的依赖注入机制。

 

 

 

类Class:

-------------------------------------------
封装:
成员变量:

成员变量是指定维度的成员变量,用于标识某个维度成员。

简介


由 Analysis Services 内部使用的值,以标识某个维度成员。MemberKeyColumn 属性指定维度的成员变量。例如,1 到 12 之间的某个数字可以是相应于年中的某个月的成员变量。
 

实例


java成员变量和方法 成员变量:在类体的变量部分中定义的变量,也称为字段。我们先来看一个Clothes类的成员变量:
package ch05.sample;
public class Clothes
{
String id; //实例变量
private String colorType; //实例变量
private int size; //实例变量
private static String depart; //类变量
final String design="yangzi"; //常量
}
代码中的实例变量、类变量、常量都属于成员变量,那么其区分的依据是什么?这与变量的修饰符有关系,也就是上面代码中的private、static、final等修饰符。成员变量的修饰符见表5-1。
表5-1 成员变量的修饰符
修饰符
说明
public
成员变量可以被项目中的任何方法访问,建议尽量少用
protected
不在同一个包中的类不能访问,但子类可以访问
private
只能在同一个类中使用
static
类变量,其值为该类的所有对象共享,不会因类的对象不同而不同
final
最终成员变量,其值保持不变,即常量
transient
当对象被持久化时(例如写入数据库),该成员变量的值不需要保存
volatile
同步多线程访问的成员变量的值,以便使不同的线程总是得到
该成员变量的同一个值。关于线程,请参阅第11章
成员变量又称全局变量,定义在类中,和类的方法处于同一个层次。成员变量相当于银行中的取号机,银行中的人都可以对其使用。成员变量的语法如下:
变量修饰符 类型 变量名;


属性:

 

c# 属性 

 

属性:get { //读属性代码 } set { //写属性代码 } 
public class Person
{
private string name;
public string Name
{
   get{return  name;}
   set{ name=value;}
}
}
属性可以忽略get或set访问器,但是不能两个都忽略.
set访问器包含一个隐藏的参数value,该参数包含从客户代码传送过来的值.
公共属性及其底层类型最好使用相同的名称,因为它们之间的联系将很清晰.

字段使用camelCase(xxXxx),如dateOfBirth,而属性使用PacalCase(XxXxx),如DateOfBirth.一些开发人员喜欢在字段的开头使用下划线,如_Name,属性也应使用名词来命名.

c#通过属性特性读取和写入字段,而不直接读取和写入,以此来提供对类中字段的保护.

属性按可以访问的类型分为三种不同的类型:

一.读/写属性

    读/写属性是一个具有get()和set()访问器的属性.

  语法:  [访问修饰符] 数据类型 属性 
{
get{ };
set{ };
}

二.只读属性

    仅具有get()访问器属性称为只读属性.

语法: [访问修饰符] 数据类型 属性名

get{ };
}

三.只写属性

   仅具有set()访问器属性称为只写属性,不推荐使用只写属性.

语法: [访问修饰符] 数据类型 属性名
{
 set{ };
}
示例:

using System;

namespace Example1
{
 class Student
 {
  #region/***属性***/
  /// <summary>
  /// 姓名
  /// </summary>
  private string name;
  public string Name
  {
   get
   {
    return name;
   }

   set
   {
    if(value.length<40)
    {
     Console.WriteLine("学生姓名长度不能小于4个!");
     return;
    }
    name=value;
   }
  }
  #region

  static void Main(string[ ] args)
  {
   Student student=new Student();
   student.Name=Console.ReadLine();
  }
 }
}

 属性(property)
-充分体现了对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问,即借助于get和set对属性的值进行读写;另一方面还可以对数据的访问属性进行控制(当然也可以通过对普通域加readonly关键字来实现。
-设计原则:属性封装了对域的操作。把要访问的域设为private,通过属性中的get和set操作对域进行设置或访问。
-不能把属性作为引用类型或输出参数来进行传递。
-get方法没有参数;set方法有一个隐含的参数value。除了使用了abstract修饰符的抽象属性,每个访问器的执行体中只有分号“;”外,其他的所有属性的get访问器都通过return来读取属性的值,set访问器都通过value来设置属性的值。
-采用间接方式来访问对象的属性(间接调用get、set方法):对象.属性 = 值(调用set),变量 = 对象.属性(调用get)。
-在属性的访问声明中:
只有set访问器,表明该属性是只写的。
只有get访问器,表明该属性是只读的。
既有set访问器,又有get访问器,表明该属性是可读可写的。
private string s_filename;
       public string Filename
       {
              get
              {
                     return s_filename;
    }//get
    set
    {
          if(s_filename!=value)
          {
                s_filename = value;
     }//if
    }//set
   }//Filename
  }

l      属性和字段的比较:

Ø       属性不能使用ref/out 型参数

Ø       属性使用前必须赋值

属性使用前必须赋值,例如:

Time lunch;

lunch.Hour = 12;//错误,lunch没有初始化

属性vs.函数

l       相似点

Ø       都包含执行代码

Ø       都可以有访问修饰符

Ø       都可以有virtual, abstract, override 修饰符

Ø       都可以用在接口中

l       不同点

Ø       属性只能拥有get/set 语句

Ø       属性不可以是void 型

Ø       属性不能使用参数

Ø       属性不能使用[ ] 参数

Ø       属性不能使用括号
public int Hour

{  

...

set 
{

if (value < 0 || value > 24)

throw new ArgumentException("value");

hour = value;





类的属性称为智能字段,类的索引器称为智能数组。由于类本身作数组使用,所以用
this作索引器的名称,索引器有索引参数值。例:
using System;
using System.Collections;

class MyListBox
{
  protected ArrayList data = new ArrayList();
  public object this[int idx]  //this作索引器名称,idx是索引参数
  {
    get
    {
      if (idx > -1 && idx < data.Count)
      {
        return data[idx];
      }
      else
      {
        return null;
      }
    }
    set
    {
      if (idx > -1 && idx < data.Count)
      {
        data[idx] = value;
      }
      else if (idx = data.Count)
      {
        data.Add(value);
      }
      else
      {
        //抛出一个异常
      }
    }
  }
}

 



尽可能编写出运行效率更高,更健壮,更容易维护的C#代码。

尽可能的使用属性(property),而不是数据成员(field)。

private   int   property1   
  public     int   Property1   
    {     
        get   
        {   
          return   property1   ;   
          }   
        set   
          {   
            if   (value>1)     //这里校验   
                property1=   value   ;   
                else     
                property1=1;   
          }
 }


 //实例属性,可读可写
  public int StrCount
  {
   get 
   {
    return m_strCount;
   }
   set
   {
    if (value>m_strCount)
    {
     strArray = new string[value];
     for (int i=0;i<value;i++)
     {
      strArray[i] = String.Format("String No.{0}",i);
     }
     m_strCount = value;
    }
   }
  }

  private static string m_strName = "MyClass";
  //一个静态属性,只读
  public static string ClassName
  {
   get
   {
    return m_strName;
   }
  }


class B
{
private A _a;
public A item
{
  get
  {
    if(_a=null)
      _a=new A();
    return _a;
  }
  set{_a=value;}
}
}

 

成员方法:

 
成员变成员变量、实例变量、类变量、成员方法、实例方法、类方法的区别 量、实例变量、类变量、成员方法、实例方法、类方法的区别

简单来说:

类体的定义包括成员变量的定义和方法的定义。

1、成员变量包括实例变量和类变量;而成员方法包括实例方法、类方法,当然还有一种特殊的构造方法。

2、类变量、类方法就是类中的变量、方法,必须是静态的,要加static;故其又称静态变量、静态方法。

3、成员变量、成员方法是对象或实例中的变量、方法,不加static;

类变量:静态域,静态字段,或叫静态变量,它属于该类所有实例共有的属性,在内存中只有一个地方存储这个变量。而且所有的实例都可以修改这个类变量的值(前提是这个类变量没有被final修饰,否则是常量了),而且访问类变量的时候不用实例,直接用类就可以了。

类方法:和类变量一样,可以不用实例,直接用类就可以调用类方法。

实例变量:实例域,实例字段,或叫成员变量。

实例方法:或叫成员方法,必须先有实例,然后才能通过实例调用该实例方法。

使用方法:类方法可以直接调用类变量和类方法

               类方法不可以直接调用实例变量和实例方法

                类方法中没有this,因为没有实例,this不知道调用哪个实例

               类方法可以从类里面直接访问类成员

               实例方法可以调用类方法,访问类变量,但是不提倡这样做,会把类方法和类变量混淆成实例方法和实例变量

程序实例:

class AnIntegerNamedX {
    int x;       //这个是成员变量
     public int x() {     //成员方法
            return x;

   }

   public void setX(int newX) {        //成员方法
            x = newX;
    }
}

public class Practice{
   public static void main(String args[]){
          AnIntegerNamedX myX = new AnIntegerNamedX();
          AnIntegerNamedX anotherX = new AnIntegerNamedX();

           myX.setX(1);
            anotherX.x = 2;

          System.out.println("myX.x = " + myX.x());
         System.out.println("anotherX.x = " + anotherX.x());

   }
}

2:

class AnIntegerNamedX {
   static int x;            //这里写成static,结果就变成全是2了,因为类变量是被实例所共享的,包括myX和anotherX

                                  调用anotherX时所有的所有实例都改变了。

public int x() {
         return x;

}

public void setX(int newX) {
       x = newX;
}
}
public class Practice{
     public static void main(String args[]){
           AnIntegerNamedX myX = new AnIntegerNamedX();
            AnIntegerNamedX anotherX = new AnIntegerNamedX();

            myX.setX(1);
            anotherX.x = 2;         //这里把X的值改成2,所有实例的X都是2了,因为类变量是被所有实例共享的,任何实

                                              例都可以对类变量做最终的修改

             System.out.println("myX.x = " + myX.x());
             System.out.println("anotherX.x = " + anotherX.x());

      }
}

       

3:同样的,当把那两个方法都加上static变成类方法的时候,会提示错误让你把X变成静态,是应为类方法不能直接访问实例变量! 


构造函数:

 构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载

特点

编辑
1.构造函数的命名必须和类名完全相同。在java中普通函数可以和构造函数同名,但是必须带有返回值;
2.构造函数的功能主要用于在类的对象创建时定义初始化的状态。它没有返回值,也不能用void来修饰。这就保证了它不仅什么也不用自动返回,而且根本不能有任何选择。而其他方法都有返回值,即使是void返回值。尽管方法体本身不会自动返回什么,但仍然可以让它返回一些东西,而这些东西可能是不安全的;
3.构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用;而一般的方法是在程序执行到它的时候被调用的;
4.当定义一个类的时候,通常情况下都会显示该类的构造函数,并在函数中指定初始化的工作也可省略,不过Java编译器会提供一个默认的构造函数.此默认构造函数是不带参数的。而一般的方法不存在这一特点;
5.构造函数有回滚的效果,构造函数抛出异常时,构造的是一个不完整对象,会回滚,将此不完整对象的成员释放(c++)
6.当一个类只定义了私有的构造函数,将无法通过new关键字来创建其对象,当一个类没有定义任何构造函数,C#编译器会为其自动生成一个默认的无参的构造函数。[1] 

应用

编辑
C++构造函数
C++语言为类提供的构造函数可自动完成对象的初始化任务,全局对象和静态对象的构造函数在main()函数执行之前就被调用,局部静态对象的构造函数是当程序第一次执行到相应语句时才被调用。然而给出一个外部对象的引用性声明时,并不调用相应的构造函数,因为这个外部对象只是引用在其他地方声明的对象,并没有真正地创建一个对象。
C++的构造函数定义格式为:
class <类名>
{
public:
<类名>(参数表)
//...(还可以声明其它成员函数)
};
<类名>::<函数名>(参数表)
{
}
如以下定义是合法的:
class T
{
public:
T(int a=0){i=a;}//构造函数允许直接写在类定义内,也允许有参数表。
private:int i;
};
如果一个类中没有定义任何的构造函数,那么编译器只有在以下三种情况,才会提供默认的构造函数:
1、如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;
2、如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);
3、在类中的所有非静态的对象数据成员,它们对应的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。
<类名>::<类名>(){},即不执行任何操作。
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
using namespace std;
class time
{
public:
time()//constructor.构造函数
{
hour=0;
minute=0;
sec=0;
}
void set_time();
void show_time();
private:
int hour,minute,sec;
};
int main()
{
class time t1;
t1.show_time();
t1.set_time();
t1.show_time();
return 0;
}
void time::set_time()
{
cin>>hour>>minute>>sec;
}
void time::show_time()
{
cout<<hour<<":"<<minute<<":"<<sec<<endl;
}
程序运行情况:
0:0:0
10 11 11 回车
10:11:11
任何时候,只要创建类或结构,就会调用它的构造函数。类或结构可能有多个接受不同参数的构造函数。构造函数使得程序员可设置默认值、限制实例化以及编写灵活且便于阅读的代码。
构造函数
void__construct( [mixed args [, ...]] )
php 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
注:如果子类中定义了构造函数则不会暗中调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用parent::__construct()。
例子 19-6. 使用新标准的构造函数
1
2
3
4
5
6
7
8
9
<?php
classBaseClass{
function__construct(){
print"InBaseClassconstructor/n";}}
classSubClassextendsBaseClass{
function__construct(){
parent::__construct();print"InSubClassconstructor/n";}}
$obj=newBaseClass();$obj=newSubClass();
?>
为了实现向后兼容性,如果 php 5 在类中找不到__construct()函数,它就会尝试寻找旧式的构造函数,也就是和类同名的函数。因此唯一会产生兼容性问题的情况是:类中已有一个名为__construct()的方法,但它却又不是构造函数。

析构函数

编辑
void__destruct( void )
php 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
例子 19-7. 析构函数示例
<?php
class MyDestructableClass {
function __construct() {
print "In constructor/n";
$this->name = "MyDestructableClass";
}
function __destruct()
{
print "Destroying " . $this->name . "/n";
}
}
$obj = new MyDestructableClass();
?>
和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用parent::__destruct()。
注:析构函数在脚本关闭时调用,此时所有的头信息已经发出。
注:试图在析构函数中抛出一个异常会导致致命错误。
构造函数的声明与其它操作的声明一样,只是其名称必须是两个下划线__construct( )。这是PHP5中的变化;PHP4的版本中,构造函数的名称必须与类名相同。为了向下兼容,如果一个类中没有名为__construct( )的方法,PHP将搜索一个与类名相同的方法。
格式:function __construct ( [参数] ) { ... ... }
例子:
<?php
class person{
public $name;
public $age;
function _ _construct(){ // 构造函数
$this->name="lisi";
$this->age=28;
}
function say(){
echo "my name is ".$this->name."<br>";
echo "my age is ".$this->age."<p>";
}
}
$per=new person();
$per->say();
$per->name="zhangsan";
$per->age=26;
$per->say();
?>
C#构造函数例子
构造函数是在创建给定类型的对象时执行的类方法。构造函数具有与类相同的名称,它通常初始化新对象的数据成员
在下面的示例中,使用一个简单的构造函数定义了名为 Taxi 的类。然后使用 new运算符来实例化该类。在为新对象分配内存之后,new运算符立即调用 Taxi 构造函数。
publicclass Taxi
{
public bool isInitialized;
public Taxi()
{
isInitialized = true;
}
}
class TestTaxi
{
static void Main()
{
Taxi t = new Taxi();
System.Console.WriteLine(t.isInitialized);
}
}
javascript实例
在本例中,我们将展示如何使用 constructor 属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<html>
 
<body>
 
 
<script type="text/javascript">
 
 
var test=newBoolean();
 
 
if(test.constructor==Array)
 
{
 
document.write("ThisisanArray");
 
}
 
if(test.constructor==Boolean)
 
{
 
document.write("ThisisaBoolean");
 
}
 
if(test.constructor==Date)
 
{
 
document.write("ThisisaDate");
 
}
 
if(test.constructor==String)
 
{
 
document.write("ThisisaString");
 
}
 
 
</script>
 
 
</body>
 
</html>
JAVA例子
public class UserManagerImpl implements UserManager {
private UserDao userDao;
public UserManagerImpl(UserDao userDao){
this.userDao=userDao;
}
public void save(String username,String password){
this.userDao.save(username, password);
}
}
注意: 由于Java不允许像C++那样为函数的参数提供缺省值,因此在构造函数中也不能使用缺省值.
构造函数的特点:
1.它的函数名与类名相同;
2.它可以重载;
3.不能指定返回类型,即使是void也不行;
4.虽然在一般情况下,构造函数不被显式调用,而是在创建对象时自动被调用。但是并不是不能被显式调用。有些时候是一定要显式调用的,只要是父类有带参的构造函数,在子类中就必须显式的调用父类的构造函数,因为子类的构造器在实例化时无法找到父类的构造函数(当父类有自己写的无参构造函数时,子类也不用显式调用)。

 

posted @ 2016-06-16 22:24  右掱写爱  阅读(254)  评论(0编辑  收藏  举报