用《叩响C#之门》复习C#基础知识 第八章 面向对象编程:类和对象(二)

1、以对象为成员

类的成员不光可以是int、double等基本类型的变量,也可以是其他类的对象。其实也就是说,类的成员可以是所有的值类型和引用类型的成员变量。

2、静态成员

1)静态变量:描述类的整体特征的量可以用静态变量实现,静态变量在内存中只有一份,为类的所有对象共享。

静态变量是描述整个类的,不管实例化多少个对象,在内存中只存在一份数据,所有的对象都可以使用它。

使用静态成员不需要声明对象,只需使用类名,比如Math类的成员基本上都是静态成员,不必声明Math类的对象即可使用各种数学常量和函数。

静态变量只能在创建类时初始化一次,如果静态变量比较复杂,可以定义一个静态构造函数,用来专门初始化静态变量。静态构造函数也需用关键字static声明。

类的所有对象共享静态变量,也就是说对象可以通过类中的相关的函数来访问静态变量(也可以通过制作静态变量对应的属性来访问和修改,这个属性也可以定义为静态的或非静态的)。因此在类外,可以用静态方法(或静态属性,属性其实也是方法)直接访问对应的类中静态变量,也可用对象通过类中的非静态函数(或非静态属性)访问静态变量。

 

没有用static关键字修饰的变量称为实例变量。实例变量通过对象名引用,静态变量通过类名引用(需继续深入!)。

 

2)静态函数

在实际使用时,静态方法只能访问类中的静态成员变量,而静态方法内定义的变量属于局部变量,局部变量不是类的成员,只是一段代码块中用到的变量而已,不存在静态的说法!

 

3、常量成员

1)const常量成员

只能在声明的时候初始化,不能在其他地方赋值,运行过程中它的值保持不变。

类的const常量成员是隐式静态的,所有的对象都可以通过相关成员函数访问它,类外要通过类名来引用const常量。

虽然const常量默认是静态的,但不能用static关键字显式声明

 

常量成员在访问上与静态成员变量方法类似,只是const常量成员只读,不可写而已。另外,const还可用于函数中局部常量声明,而static只能针对类中的成员定义时用。

 

2)readonly常量

const常量是隐式静态的,为同一个类的全部对象所共有,所有对象具有相同的值。

然而,在现实中,我们还需要一种常量,它在类的具体对象中是固定的常数,但在不同对象中可能是不同的值。这种常量用readonly常量实现。

声明readonly常量语法:

访问修饰符 readonly关键字 类型 常量名;

与const常量不同,readonly常量是非静态常量,没有要求在类中声明时赋值,每个对象可以有不同的值,我们把readonly常量初始化代码放在构造函数里,这样每个对象就可以有不同的值。(在类中除了构造函数或变量初始值指定项,无法对只读的字段赋值,变量初始值指定项指的是啥?在此存留疑问!)

readonly常量如果被定义为静态常量,即在前面加static,称为静态只读字段,此时只能在静态构造函数或变量初始值指定项中对其进行赋值。

 

针对2、3两点值得深入

 

4、重载

1)函数重载的调用原则是参数“最佳匹配(Best-fit)”,即系统调用参数类型最匹配的那个函数(参数数量、类型匹配的那个函数)。

2)构造函数的重载,这些构造函数均和类同名,但参数类型不同,系统自动调用参数完全匹配的那个构造函数。

3)运算符重载,由关键字operator声明,必须定义为静态!

访问修饰符(一般为public) static 返回类型 operator关键字 运算符(如+) (左操作数类型 左操作数变量名,右操作数类型 右操作数变量名)

例子: public static A类名 operator + (A类名 a1,A类名 a2)

重载运算符的调用原则也是参数的“最佳匹配”,即系统是根据左右两个操作数的类型选择调用哪个版本的运算符。运算符见过去的随笔

需要注意的是:&是按位与,|是按位或,^是按位异或,>>右移运算符,<<左移运算符,前面这5个都是二元位运算符;~是按位求补,是一元位运算符;比较运算符==和!=,>=和<,<=和>必须成对重载。

另外,不必重载“+=”、“&=”等这类复合运算符,因为A+=B会自动解释为A=A+B,所以只要重载好+运算符就行了。

 

5、this关键字

在类外要通过对象名来访问类的成员,但在类的定义代码中,可以直接使用所有成员,其实每个对象都有一个指向自己的this引用符,一般情况下,在类的内部,你可以直接用类的成员,也可以通过this引用符使用变量。

 

6、索引

索引可以让我们像数组那样访问类的数据成员。索引的函数体与属性类似,也是get和set访问器,get访问器用于获取成员变量的值,set访问器用于为成员变量赋值。

定义方式如下:

访问修饰符 数据类型 this 关键字 [下标类型 下标名称]   这里的[]是[]运算符。

在数组中,下标只能为整数,在索引中,下标既可以为int型,也可以为double、string等类型!

C#还为我们提供了多维索引,只需提供多个下标即可。要用嵌套的switch语句实现。

 

7、值类型和引用类型

1)值类型变量

内存中有一块区域成为栈(stack),用来存储整型、实型、布尔型、字符型等基本类型数据。栈的压入、弹出数据的操作总是发生在栈的顶部。

操作系统通过栈指针中存储的地址读写栈中的数据,当栈为空时,栈指针指向栈的底部,随着数据的不断入栈,栈指针不断向栈顶部移动,始终指向栈中下一块自由空间。

当程序执行代码块,如执行到“float a=10.0F”时,将变量a的值压入栈,同时指针向顶部移动4个字节,其他语句涉及到值类型变量赋值后,继续将相关值压入栈;当退出代码块时,程序依次将变量从栈顶部移出,直至栈指针指回栈底。

栈对数据的操作总发生在栈的顶部,最后入栈的变量最先弹出,最先入栈的数据最后弹出,因此先入栈的数据作用域总比后入栈的要长,后入栈数据的作用域嵌套在先入栈数据之中。栈的这种工作方式成为后入先出(Last In First Out,LIFO)。

整型、实型、布尔型、字符型等简单数据和结构体存储在栈中,称为值类型变量(Value type)。

注意:C#不能使用未赋值的变量!只有赋过值的变量才能被使用。

 

2)引用型变量

栈有非常高的性能,但栈中的变量生存周期都是嵌套的,在类中,我们希望构造函数创建成员变量后,即使退出构造函数,这些变量仍然存在,其他函数仍然可以使用这些变量,为此C#把类的成员变量存储在堆(Heap)上。

当创建了A类的对象时,系统做以下两件事:

a、系统在堆中划分一块空间用于存储对象的成员变量,并调用构造函数初始化对象的成员变量。

b、系统在栈中分配4字节(32位操作系统,即4字节的寻址)的空间,存储对象在堆中的首地址。

 

栈中存储的指向堆中对象的地址称为引用符(Reference),系统通过引用符找到堆中的对象。

所有对象都存储在堆中,数组也存储在堆中,它们都称为引用型变量(Reference type)。创建引用型变量比创建值类型变量复杂的多,虽然会造成一点性能上的损失,但可以对数据的生存周期进行非常强大的控制。

 

3)引用型变量和垃圾回收

在实际过程中,可能会有多个引用符指向同一个对象,当一个引用符退出作用域时,系统就会从栈中删除该引用符。当指向对象的所有引用符都被删除时,该对象就被加入垃圾回收的候选名单,垃圾回收器会在适当的时候清除该对象,只要有引用符指向对象,对象就不会被清除。

在.NET中使用的是托管堆,当垃圾回收器清除一个对象后,垃圾回收器会移动堆中其他对象,使它们连续地排列在堆的底部,并用一个堆指针指向空闲空间。当创建新的对象时,系统根据堆指针可以直接找到自由的内存空间。

垃圾回收器的整理工作是托管堆和传统堆的区别所在,使用托管堆时,创建对象要快很多,虽然删除对象时整理操作会浪费一定时间,但这些损失在其他地方得到很好的补偿。

 

8、引用符和对象的区别

只有使用new运算符后,对象才真正被创建于堆中,才能使用对象的成员。当声明一个对象时,那只是声明了一个对象的名称,仅在栈上创建了一个引用符而已,并没有真的在堆中创建对象,对象的各成员是不存在的。

当对象没有实例化时,引用符的值为null(空),当对象已经实例化,引用符的值就是该对象在堆中的地址。通过引用符中存储的地址,系统可以轻易找到所要的对象。引用符是对象在内存中的地址,这一点在多态性中有非常明显的体现。

 

9、声明对象数组

类名[] 引用符=new 类名[对象元素的数量];

这只是声明了一组引用符而已,原文中认为没有真正创建对象,应该是不妥的(这句我写错了,作者已经亲自上门指导,非常感谢!),应该是及创建了一组引用符,也创建了与各引用符关联的对象。

using System;
using System.Collections.Generic;
namespace Test
{
    public class Cat
    {
        private string name;
        string Name
        {
            get
            {
                return this.name;
            }
            set 
            {
                name = value;
            }
        }
        public Cat(string nameVar)
        {
            this.Name = nameVar;
        }
        public void DisplayName()
        {
            Console.WriteLine(this.Name);
        }

    }
    public class Program
    {
        static void Main()
        {
            Cat[] cat=new Cat[7];
            for(int i=0;i<7;i++)
            {
                cat[i]=new Cat("Cat"+i);
            }
            foreach(Cat catVar in cat)
            {
                catVar.DisplayName();
            }
            
        }
            
            
        
    }
}

输出:

image 可见应该是及创建了一组引用符,也创建了与各引用符关联的对象。(这句我写错了,作者已经亲自上门指导,非常感谢!)

posted on 2009-10-14 22:07  友闻语上  阅读(418)  评论(2编辑  收藏  举报