不同的是,按值传递方式中,函数部分不能改变主函数中实参的值。而按地址传递和按引用传递均可以改变主函数中实参的值。
按值传递,实参和形参均为同一类型的对象。例如:
void f(int b)
{...}
int main()
{
int a;
...
f(a);
}
按地址传递,实参为变量的地址,而形参为同类型的指针。
void f(int* b)
{...}
int main()
{
int a;
...
f(&a);
}
按引用传递,实参为变量,形参为同类型的引用。
void f(int& b)
{...}
int main()
{
int a;
...
f(a);
}
在C++中,通过使用运算符new和delete可以进行内存空间的动态分配和释放。比如给一个double类型的指针进行内存空间的分配和释放操作,可以采用下面的代码:
如果需要分配和释放的内存空间是一个数组,可以采用下面的代码:
|
void 指针
void 指针
在C++语言中,关键字void的用法比较多。比如,在函数的原型(proto type)或定义(definition)处,如果在函数名前加一个关键字void,则表示该函数没有返回值,在函数体中退出时只要使用return语句,并且后面不带返回值就可以了。如果在函数的参数列表中只有一个关键字void,则表明该函数没有参数。 #pragma argsused void showhex(void *pv, int size) int main(int argc, char* argv[]) 运行结果 78 56 34 12
typeid运算符
可以使用typeid运算符来求得变量或对象的数据类型。typeid运算符对变量或数据类型作用后,将返回一个type_info类型的对象,通过该对象,就可以获取数据类型的名称。type_info类一般在头文件typeinfo.h中定义。在C++中,相应类的定义如下: 例程S 0 2 H的源代码 同模板函数类似,重载函数的机制也可以定义一系列功能相似的函数,并且这一系列函数的函数名是相同的,只是参数列表和返回值不同。通常将这一系列的函数称为一个重载函数家族。 例程S 0 2 L的源代
|
const类型成员函数与mutable
const类型的成员函数是指使用const关键字进行修饰的类的成员函数。const类型的成员函数对函数内部的操作加以一定的限制,比如不可以对对象的属性进行修改等,这样可以提高程序代码的正确性。
在使用关键字const对成员函数修饰的时候,将const放在成员函数的后面。在定义对象的时候,可以使用关键字const声明对象为常量对象。对于常量对象,如果使用非常量成员函数来操作,则会出现一个编译错误;同样,对于使用关键字volatile声明的对象,
如果使用非volatile类型的成员函数来操作,也会出现一个编译错误。
如果想修改const类型对象的成员变量,可以使用关键字mutable对该成员变量进行修饰。
#include <iostream.h>
#include <conio.h>
class CDate
{
public:
int year;
mutable int month; // 使用关键字mutable进行修饰
CDate(int y=2000,int m=1)
{
year= y;
month= m;
};
int GetMonth() const; // 一个只读函数
void SetMonth( int m ); // 一个写函数
};
int CDate::GetMonth() const
{
// 下面一行错误,故屏蔽掉
// year++; // 但如果是month+ + ;则没问题
return month; // 在只读函数中不可以进行改写操作
}
void CDate::SetMonth(int m)
{
month = m; // 设置月份
}
void main()
{
CDate d1;
d1.Setmonth(9);
d1.year=1975 ;
cout<<"d1:"<<d1.year<<"年"<<d1.Getmonth()<<"月/n";
const CDate d2;
d2.month=7;
cout<<"d2:"<<d2.year<<"年"<<d2.Getmonth()<<"月/n";
// 下面一句错误,故屏蔽掉
// d2.SetMonth(7); // 不可以对常量对象调用非常量成员函数
// 下面一句错误,故屏蔽掉
// d2.year=1973; // 不可以修改常量对象的成员变量
}
运行结果:
d1:1975年9月
d2:2000年7月
说明:
• 在上面的CDate类中,成员变量year为普通类型的变量,而成员变量month为mutable类型的成员变量,所以成员变量month不受const的约束,这可以从上面例程中的多个方面表现。
• 在const类型的成员函数定义体中,不可以直接或间接地修改普通类型的成员变量(mutable类型的成员函数除外)。当对象被声明为const类型后,一般也不可以直接或间接地修改对象的成员变量( mutable类型的成员函数除外)。
静态成员变量与静态成员函数
在类中可以使用关键字static定义静态成员变量和静态成员函数,它们都具有特殊的用途。
当为一个类定义了一个静态成员变量后,该变量对于该类的所有对象来说,只有一个拷贝,也就是为该类的所有对象共用。基于此,静态成员变量可以用来表示一些特殊的属性,比如对于该类当前的对象来说,该属性必须是一致的。其中的某一个对象修改了该属性,则所有其他对象的该属性值都被修改了。也可以说,类的静态成员变量是一个公共变量,只不过公用的范围只限于该类的对象。
静态成员变量可以初始化和赋值。在初始化和赋值时只要利用作用域运算符“ : :”指定类名即可。对于私有类型的静态成员变量可以象公有类型的静态成员变量一样赋值,但是不可以直接引用。
另外,可以使用静态成员变量来表示当前该类所有对象的个数。在一开始将静态成员变量清零,并在类的构造函数中将此静态成员变量增加1,在析构函数中将此变量减1。这样,就可以随时确定当前对象的个数。
静态成员函数中实现的代码是一些与具体对象无关的代码,是来实现该类全体对象通用的任务。
所以,对于静态成员函数有下面的一些约束:
• 静态成员函数的定义体中不可以对非静态成员变量进行操作。
• 静态成员函数中不可以调用非静态成员函数。
• 静态成员函数中不能使用this指针。
通常,构造函数和析构函数不可以是静态成员函数。
下面通过例程说明静态成员变量和静态成员函数的用法。
#include <iostream.h>
#include <conio.h>
class Rabbit
{
public:
Rabbit(); // 默认构造函数
~ Rabbit(); // 析构函数
static int GetNum(); // 公有类型的静态成员函数
static int Num; // 公有类型的静态成员变量
};
Rabbit::Rabbit()
{
Num++;
}
Rabbit::~ Rabbit()
{
Num--;
}
int Rabbit::GetNum()
{
return Num;
}
int Rabbit::Num=0; // 对静态公有成员变量进行初始化
void main()
{
Rabbit r1,r2; // 将调用默认的构造函数
// 公有类型的静态成员变量可以直接引用
cout<<"Num of Rabbits is "<<Rabbit::Num<<"./n";
Rabbit * r3=new Rabbit; // 将调用默认的构造函数
// 通过类调用静态成员函数
cout<<"Num of Rabbits is "<<Rabbit::GetNum()<<"./n";
delete r3; // 将调用唯一的一个析构函数
// 通过对象调用静态成员函数
cout<<"Num of Rabbits is "<<r1.GetNum()<<"./n";
}
运行结果:
Num of Rabbits is 2.
Num of Rabbits is 3.
Num of Rabbits is 2.
说明:
静态成员函数的调用有可以有下面两种方法:
• 类名.函数名( );
• 对象名.函数名( );
通常建议使用第一种方式,而不要使用第二种方式,因为后者的可读性不好。
对象的引用参数传递
下面的例程将对“对象的引用参数传递”与“对象的普通参数传递”作一个定量的比较:
首先定义两个不同参数传递类型的函数,然后在主函数中获取系统时间并显示,接着多次调用使用引用参数传递的函数,最后再一次获取系统时间并显示。将运行结果记录下来后,修改代码,调用使用普通参数传递的函数,运行后记录下运行结果,与前面的记录进行比较。
例程中对如何获取系统时间也进行了说明。
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <sys/timeb.h>
#include <time.h>
class Book // 类的定义体
{
public:
int No;
Book(int n); // 构造函数
~ Book() ; // 构造函数
} ;
Book::Book(int n)
{
No=n;
}
Book::~ Book ( )
{
}
void Add(Book b) // 普通参数传递
{
b.No++;
}
void AddRef(Book & b) // 引用参数传递
{
b.No++;
}
void main()
{
Book B1(1); // 临时变量
long i;
struct timeb CurTime;
char TimeMes[100];
char *timeline;
// 获取计算前的开始时间
ftime( &CurTime);
timeline = ctime( & ( CurTime.time ) );
cout<<timeline<<endl; //当前时间 如"Fri Dec 24 13:39:29 2004"
sprintf(TimeMes,"计算开始时间是:%.19s.%hu %s ", timeline,CurTime.millitm,&timeline[20]);
cout<<TimeMes;
// 计算内容
for(i=0;i<10000000;i++)
AddRef(B1);// 修改此句为:Add(B1);
cout<<B1.No<<endl;
// 获取计算后的结束时间
ftime( &CurTime);
timeline = ctime(&(CurTime.time));
sprintf(TimeMes,"计算结束时间是:%.19s.%hu %s ", timeline,CurTime.millitm,&timeline[20]);
cout<<TimeMes;
getch();
}
运行结果:
Fri Dec 24 13:46:57 2004
计算开始时间是:Fri Dec 24 13:46:57.20 2004
10000001
计算结束时间是:Fri Dec 24 13:46:57.90 2004
指向函数的指针
通过指向函数的指针,可以编写一些通用性较好的代码。
下面的例程S02M中定义了两个普通的函数,一个是求出两个整数中的最大值,另一个是求出两个整数中的最小值。例程中还定义了一个指向函数的指针,指针的类型及参数列表中各参数的类型与前面定义的两个函数是一致的,最后在主函数中通过该指针实现了一定的操作。
例程S02M的源代码
#include <iostream.h>
#include <conio.h>
// 判断两个整数的最大值
int Max(int x,int y)
{
return x>y?x:y;
}
// 判断两个整数的最小值
int Min(int x,int y)
{
return x<y?x:y;
}
// 定义指向函数的指针
int (* m_pFunction)(int,int);
void main()
{
m_pFunction = Max; // 指向求最大值的函数
cout <<"2和3中较大的是:"<<(*m_pFunction)(2,3)<<endl;
m_pFunction = Min; // 指向求最小值的函数
cout <<"2和3中较小的是:"<<(*m_pFunction)(2,3)<<endl;
getch();
}
运行结果:
2和3中较大的是: 3
2和3中较小的是: 2
说明:
如果函数指针需要指向的函数有参数列表,那么在定义指针的时候,可以不必指明形参,只要一个数据类型的列表就可以了。