C++入门 (六) 类对象数组和静态成员
2012-08-01 08:14
495 查看
一、类对象数组
类的对象和C++其他数据类型一样,也可以为其建立数组,数组的表示方法和结构一样。
#include iostream.h
class Date
{
int mo,da,yr;
public:
Date(int m=0,int d=0, int y=0) { mo=m; da=d; yr=y;}
void display() const { cout<<mo<<' '<<da<<'="" '<<yr<<endl;="" }
};
int main()
{
Date dates[2];
Date today(12,31,2003);
dates[0]=today;
dates[0].display();
dates[1].display();
return 0;
}
1.类对象数组和默认构造函数
在前面已经说过,不带参数或者所有参数都有默认值的构造函数叫做默认构造函数。如果类中没有构造函数,编译器会自动提供一个什么都不做的公共默认构造函数 。如果类当中至少有一个构造函数,编译器就不会提供默认构造函数。
如果类当中不含默认构造函数,则无法实例化其对象数组。因为实例花类对象数组的格式不允许用初始化值来匹配某个构造函数的参数表。
上面的程序中,main()函数声明了一个长度为2的Date对象数组,还有一个包含初始化值的单个Date对象。接着把这个初始化的Date对象赋值给数组中第一个对象,然后显示两个数组元素中包含的日期。从输出中可以看到,第一个日期是有效日期,而第二个显示的都是0。
当声明了某个类的对象数组时,编译器会为每个元素都调用默认构造函数。
下面的程序去掉了构造函数的默认参数值,并且增加了一个默认构造函数。
#include
class Date
{
int mo, da, yr;
public:
Date();
Date(int m,int d,int y) { mo=m; da=d; yr=y;}
void display() const { cout <<mo<<' '<<da<<'="" '<<yr<<endl;="" }
};
Date::Date()
{
cout <<date constructor="" running<<endl;
mo=0; da=0; yr=0;
}
int main()
{
Date dates[2];
Date today(12,31,2003);
dates[0]=today;
dates[0].display();
dates[1].display();
return 0;
}
运行程序,输出为:
Date constructor running
Date constructor running
12/31/2003
0/0/0
从输出中可以看出,Date()这个默认构造函数被调用了两次。
2.类对象数组和析构函数
当类对象离开作用域时,编译器会为每个对象数组元素调用析构函数。
#include iostream.h
class Date
{
int mo,da,yr;
public:
Date(int m=0,int d=0,int y=0) { mo=m; da=d; yr=y;}
~Date() {cout<<date destructor="" running<<endl;}
void display() const {cout<<mo<<' '<<da<<'="" '<<yr<<endl;="" }
};
int main()
{
Date dates[2];
Date today(12,31,2003);
dates[0]=today;
dates[0].display();
dates[1].display();
return 0;
}
运行程序,输出为:
12/31/2003
0/0/0
Date destructor running
Date destructor running
Date destructor running
表明析构函数被调用了三次,也就是dates[0],dates[1],today这三个对象离开作用域时调用的。
二、静态成员
可以把类的成员声明为静态的。静态成员只能存在唯一的实例。所有的成员函数都可以访问这个静态成员。即使没有声明类的任何实例,静态成员也已经是存在的。不过类当中声明静态成员时并不能自动定义这个变量,必须在类定义之外来定义该成员。
1.静态数据成员
静态数据成员相当于一个全局变量,类的所有实例都可以使用它。成员函数能访问并且修改这个值。如果这个静态成员是公有的,那么类的作用域之内的所有代码(不论是在类的内部还是外部)都可以访问这个成员。下面的程序通过静态数据成员来记录链表首项和末项的地址。
#include iostream.h
#include string.h
class ListEntry
{
public:
static ListEntry* firstentry;
private:
static ListEntry* lastentry;
char* listvalue;
ListEntry* nextentry;
public:
ListEntry(char*);
~ListEntry() { delete [] listvalue;}
ListEntry* NextEntry() const { return nextentry; };
void display() const { cout<<listvalue<<endl; }
};
ListEntry* ListEntry::firstentry;
ListEntry* ListEntry::lastentry;
ListEntry::ListEntry(char* s)
{
if(firstentry==0) firstentry=this;
if(lastentry!=0) lastentry->nextentry=this;
lastentry=this;
listvalue=new char[strlen(s)+1];
strcpy(listvalue,s);
nextentry=0;
}
int main()
{
while (1)
{
cout<<\nEnter a name ('end' when done): ;
char name[25];
cin>>name;
if(strncmp(name,end,3)==0) break;
new ListEntry(name);
}
ListEntry* next = ListEntry::firstentry;
while (next != 0)
{
next->display();
ListEntry* hold = next;
next=next->NextEntry();
delete hold;
}
return 0;
}
程序首先显示提示信息,输入一串姓名,以end作为结束标志。然后按照输入顺序来显示姓名。构造函数将表项加入链表,用new运算符来声明一个表项,但并没有把new运算符返回的地址赋值给某个指针,这是因为构造函数会把该表项的地址赋值给前一个表项的nextentry指针。
这个程序和前面将的逆序输出的程序都不是最佳方法,最好的方法是使用类模板,这在后面再介绍。
main()函数取得ListEntry::firstentry的值,开始遍历链表,因此必需把ListEntry::firstentry设置成公有数据成员,这不符合面向对象程序的约定,因为这里数据成员是公有的。
2.静态成员函数
成员函数也可以是静态的。如果一个静态成员函数不需要访问类的任何实例的成员,可以使用类名或者对象名来调用它。静态成员通常用在只需要访问静态数据成员的情况下。
静态成员函数没有this指针,因为它不能访问非静态成员,所以它们不能把this指针指向任何东西。
下面的程序中,ListEntry类中加入了一个静态成员函数FirstEntry(),它从数据成员firstentry获得链表第一项的地址,在这儿,firstentry已经声明为私有数据成员了。
#include iostream.h
#include string.h
class ListEntry
{
static ListEntry* firstentry;
static ListEntry* lastentry;
char* listvalue;
ListEntry* nextentry;
public:
ListEntry(char*);
~ListEntry() { delete [] listvalue;}
static ListEntry* FirstEntry() { return firstentry; }
ListEntry* NextEntry() const { return nextentry; };
void display() const { cout<<listvalue<<endl; }
};
ListEntry* ListEntry::firstentry;
ListEntry* ListEntry::lastentry;
ListEntry::ListEntry(char* s)
{
if(firstentry==0) firstentry=this;
if(lastentry!=0) lastentry->nextentry=this;
lastentry=this;
listvalue=new char[strlen(s)+1];
strcpy(listvalue, s);
nextentry = 0;
}
int main()
{
while (1)
{
cout<<\nEnter a name ('end' when done):;
char name[25];
cin >> name;
if(strncmp(name,end,3)==0) break;
new ListEntry(name);
}
ListEntry* next = ListEntry::FirstEntry();
while (next != 0)
{
next->display();
ListEntry* hold = next;
next = next->NextEntry();
delete hold;
}
return 0;
}
函数ListEntry::FirstEntry()是静态的,返回静态数据成员firstentry的值。
3.公有静态成员
如果一个静态成员象上面程序一样是公有的,那么在整个程序中都可以访问它。可以在任何地方调用公有景泰成员函数,而且不需要有类的实例存在。但公有静态成员函数不完全是全局的,它不仅仅存在于定义类的作用域内。在这个作用域里面,只要在函数名前加上类名和域解析运算符::就可以调用该函数。
类的对象和C++其他数据类型一样,也可以为其建立数组,数组的表示方法和结构一样。
#include iostream.h
class Date
{
int mo,da,yr;
public:
Date(int m=0,int d=0, int y=0) { mo=m; da=d; yr=y;}
void display() const { cout<<mo<<' '<<da<<'="" '<<yr<<endl;="" }
};
int main()
{
Date dates[2];
Date today(12,31,2003);
dates[0]=today;
dates[0].display();
dates[1].display();
return 0;
}
1.类对象数组和默认构造函数
在前面已经说过,不带参数或者所有参数都有默认值的构造函数叫做默认构造函数。如果类中没有构造函数,编译器会自动提供一个什么都不做的公共默认构造函数 。如果类当中至少有一个构造函数,编译器就不会提供默认构造函数。
如果类当中不含默认构造函数,则无法实例化其对象数组。因为实例花类对象数组的格式不允许用初始化值来匹配某个构造函数的参数表。
上面的程序中,main()函数声明了一个长度为2的Date对象数组,还有一个包含初始化值的单个Date对象。接着把这个初始化的Date对象赋值给数组中第一个对象,然后显示两个数组元素中包含的日期。从输出中可以看到,第一个日期是有效日期,而第二个显示的都是0。
当声明了某个类的对象数组时,编译器会为每个元素都调用默认构造函数。
下面的程序去掉了构造函数的默认参数值,并且增加了一个默认构造函数。
#include
class Date
{
int mo, da, yr;
public:
Date();
Date(int m,int d,int y) { mo=m; da=d; yr=y;}
void display() const { cout <<mo<<' '<<da<<'="" '<<yr<<endl;="" }
};
Date::Date()
{
cout <<date constructor="" running<<endl;
mo=0; da=0; yr=0;
}
int main()
{
Date dates[2];
Date today(12,31,2003);
dates[0]=today;
dates[0].display();
dates[1].display();
return 0;
}
运行程序,输出为:
Date constructor running
Date constructor running
12/31/2003
0/0/0
从输出中可以看出,Date()这个默认构造函数被调用了两次。
2.类对象数组和析构函数
当类对象离开作用域时,编译器会为每个对象数组元素调用析构函数。
#include iostream.h
class Date
{
int mo,da,yr;
public:
Date(int m=0,int d=0,int y=0) { mo=m; da=d; yr=y;}
~Date() {cout<<date destructor="" running<<endl;}
void display() const {cout<<mo<<' '<<da<<'="" '<<yr<<endl;="" }
};
int main()
{
Date dates[2];
Date today(12,31,2003);
dates[0]=today;
dates[0].display();
dates[1].display();
return 0;
}
运行程序,输出为:
12/31/2003
0/0/0
Date destructor running
Date destructor running
Date destructor running
表明析构函数被调用了三次,也就是dates[0],dates[1],today这三个对象离开作用域时调用的。
二、静态成员
可以把类的成员声明为静态的。静态成员只能存在唯一的实例。所有的成员函数都可以访问这个静态成员。即使没有声明类的任何实例,静态成员也已经是存在的。不过类当中声明静态成员时并不能自动定义这个变量,必须在类定义之外来定义该成员。
1.静态数据成员
静态数据成员相当于一个全局变量,类的所有实例都可以使用它。成员函数能访问并且修改这个值。如果这个静态成员是公有的,那么类的作用域之内的所有代码(不论是在类的内部还是外部)都可以访问这个成员。下面的程序通过静态数据成员来记录链表首项和末项的地址。
#include iostream.h
#include string.h
class ListEntry
{
public:
static ListEntry* firstentry;
private:
static ListEntry* lastentry;
char* listvalue;
ListEntry* nextentry;
public:
ListEntry(char*);
~ListEntry() { delete [] listvalue;}
ListEntry* NextEntry() const { return nextentry; };
void display() const { cout<<listvalue<<endl; }
};
ListEntry* ListEntry::firstentry;
ListEntry* ListEntry::lastentry;
ListEntry::ListEntry(char* s)
{
if(firstentry==0) firstentry=this;
if(lastentry!=0) lastentry->nextentry=this;
lastentry=this;
listvalue=new char[strlen(s)+1];
strcpy(listvalue,s);
nextentry=0;
}
int main()
{
while (1)
{
cout<<\nEnter a name ('end' when done): ;
char name[25];
cin>>name;
if(strncmp(name,end,3)==0) break;
new ListEntry(name);
}
ListEntry* next = ListEntry::firstentry;
while (next != 0)
{
next->display();
ListEntry* hold = next;
next=next->NextEntry();
delete hold;
}
return 0;
}
程序首先显示提示信息,输入一串姓名,以end作为结束标志。然后按照输入顺序来显示姓名。构造函数将表项加入链表,用new运算符来声明一个表项,但并没有把new运算符返回的地址赋值给某个指针,这是因为构造函数会把该表项的地址赋值给前一个表项的nextentry指针。
这个程序和前面将的逆序输出的程序都不是最佳方法,最好的方法是使用类模板,这在后面再介绍。
main()函数取得ListEntry::firstentry的值,开始遍历链表,因此必需把ListEntry::firstentry设置成公有数据成员,这不符合面向对象程序的约定,因为这里数据成员是公有的。
2.静态成员函数
成员函数也可以是静态的。如果一个静态成员函数不需要访问类的任何实例的成员,可以使用类名或者对象名来调用它。静态成员通常用在只需要访问静态数据成员的情况下。
静态成员函数没有this指针,因为它不能访问非静态成员,所以它们不能把this指针指向任何东西。
下面的程序中,ListEntry类中加入了一个静态成员函数FirstEntry(),它从数据成员firstentry获得链表第一项的地址,在这儿,firstentry已经声明为私有数据成员了。
#include iostream.h
#include string.h
class ListEntry
{
static ListEntry* firstentry;
static ListEntry* lastentry;
char* listvalue;
ListEntry* nextentry;
public:
ListEntry(char*);
~ListEntry() { delete [] listvalue;}
static ListEntry* FirstEntry() { return firstentry; }
ListEntry* NextEntry() const { return nextentry; };
void display() const { cout<<listvalue<<endl; }
};
ListEntry* ListEntry::firstentry;
ListEntry* ListEntry::lastentry;
ListEntry::ListEntry(char* s)
{
if(firstentry==0) firstentry=this;
if(lastentry!=0) lastentry->nextentry=this;
lastentry=this;
listvalue=new char[strlen(s)+1];
strcpy(listvalue, s);
nextentry = 0;
}
int main()
{
while (1)
{
cout<<\nEnter a name ('end' when done):;
char name[25];
cin >> name;
if(strncmp(name,end,3)==0) break;
new ListEntry(name);
}
ListEntry* next = ListEntry::FirstEntry();
while (next != 0)
{
next->display();
ListEntry* hold = next;
next = next->NextEntry();
delete hold;
}
return 0;
}
函数ListEntry::FirstEntry()是静态的,返回静态数据成员firstentry的值。
3.公有静态成员
如果一个静态成员象上面程序一样是公有的,那么在整个程序中都可以访问它。可以在任何地方调用公有景泰成员函数,而且不需要有类的实例存在。但公有静态成员函数不完全是全局的,它不仅仅存在于定义类的作用域内。在这个作用域里面,只要在函数名前加上类名和域解析运算符::就可以调用该函数。
相关文章推荐
- C++ 类 & 对象-C++ 内联函数-C++ this 指针-C++ 类的静态成员
- C++初始化列表问题,类中有一个对象类型的数组成员变量,在初始化列表中初始化时报错“[]”操作符语法错误
- 《C++第七周实验报告1-1》---含有静态数据成员和成员函数的Time类:类中所有的对象共有的数据
- C++面向对象编程:对象的内存分配与静态成员
- C和C++的面向对象专题(7)——单例模式解决静态成员对象和全局对象的构造顺序难题
- C++基础学习笔记----第十一课(类的静态成员、对象模型初步认识)
- C++:类与对象6(静态成员:平均分)
- C++对象的生存周期和类的静态成员
- C++第七周任务【任务1】含有静态数据成员和成员函数的Time类:类中所有的对象共有的数据
- 类中定义数组提示:静态成员引用必须与特定对象相对
- 【c++程序】在数组对象中初始化成员变量
- C++对象数组和成员对象
- C++中的数组和局部静态对象
- 深度探索C++对象模型之:成员函数语义学--静态成员函数
- C++静态成员函数不能声明为const、volatile、virtual的原因 与 C++的对象模型
- c++のstatic静态成员、对象的动态创建和释放
- 2013级C++第4周(春)项目——再和对象找感觉【项目4 - 数组作数据成员】
- 关于C++中用静态成员统计实例化对象个数问题_CVTE技术面问题
- 【C++基础学习】成员对象与对象数组
- 第05章 CORE C++_对象的创建和使用_继承_多态_析构_xxx_cast_友元_只读成员_静态成员_多重继承_虚继承_内部类