构造函数
构造函数就是构造类的实例时,系统自动调用的成员函数。当一个对象被创建时,它是否能够被正确的初始化,在c++中是通过构造函数来解决的。每当对象被声明或者在堆栈中被分配时,构造函数即被调用。构造函数是一种特殊的类成员,其函数名和类名相同,声明格式:
《函数名》(《参数列表》);
class student
{
private:
int id;
float chinese,english,math;
public:
student();//构造函数
student(int m_id,float m_chinese,float m_english,float m_math);//构造函数,设置学号,三门课程的成绩
void show();
};
student ::student()
{
id = 0;
chinese = english = math =0;
}
student::student(int m_id, float m_chinese ,float m_english,float m_math)
{
id = m_id;
chinese = m_chinese;
english = m_english;
math =m_math;
}
void student::show()
{
cout<<id<<" "<<chinese<<" "<<english<<" "<<math<<endl;
}
分析:从输出结果来看,在main()函数中,构造函数没有被显示调用,而在初始化对象时自动调用。其中, s1调用的是不带参数的student(),s2调用的是带参数的构造函数。代码中注释掉的语句是错误的,构造函数不能被显示调用。
#include "iostream.h"
//......此处省略类student的定义
int main()
{
student s1(1,60,30,58); //显式初始化
s1.show();
student s2(s1);
s2.show();
return 0;
}
自定义拷贝构造函数的使用方法
初始化列表的使用:
#include "iostream.h"
//......省略了类student的声明和定义
//自定义拷贝构造函数在类体内的声明方式
//student(const student &S);
inline student::student(const student &s)
{
id = s.id;
chinese = s.chinese;
english = s.english;
math = s.math;
}
void main()
{
student s1(1,435,456,23);
s1.show();
student s2(s1);
s2.show();
}
输出结果:
mI(i),i= 2
mI(i),i= 3
Test()
c = 1,m1.mI = 2,m2.mI = 3
press any key to continue...
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,即复制另一个对象的构造函数。一般在下述3种情况下会用到拷贝构造函数:
1.用一个类对象显式初始化另一个类对象。
2.把类对象作为实参传递给函数:
3.将类对象作为函数的返回值时。
这些情况下,系统就会自动调用拷贝构造函数,将参数代表的对象中的属性逐个拷贝到新创建的对象中。拷贝构造函数有两种形式:系统默认形式和自定义形式。
系统默认形式的使用方法:
析构函数
析构函数就是构造类的实例时,系统自动调用成员函数。当一个对象被创建时,它是否能够被正确的初始化,在c++中是通过构造函数来解决的。
#include <iostream>
//初始化列表
class M
{
private:
int mI;
public:
M(int i)
{
printf("mI(i),i= %d\n",i);
mI=i;
}
int getI()
{
return mI;
}
};
class Test
{
private:
const int c;
M m1;
M m2;
public:
Test() : c(1),m2(3),m1(2)
{
printf("Test()\n");
print();
}
void print()
{
printf("c = %d,m1.mI = %d,m2.mI = %d\n",c,m1.getI(),m2.getI());
}
};
void run()
{
Test t1;
}
int main()
{
run();
printf("press any key to continue...");
return 0;
}
成员变量的初始化顺序和初始化列表的关系不大,初始化顺序只与成员变量的定义顺序有关。
** 类中的const成员是肯定会被分配空间的
** 类中的const成员变量只是一个只读变量。
¥ 不要在一个构造函数内调用了另外一个构造函数。
静态成员
静态成员是用static修饰的成员,属性和函数都可以被说明是静态的。被定义为静态的属性或函数,在类的各个实例间是共享的,不会为每个类的实例都创建一个静态成员的实现。静态数据成员是一种特殊的属性,在定义类对象时,不会为每个类对象复制一份静态数据成员,而是让所有的类对象都共享一份静态数据成员备份。其定义格式如下:
static《数据类型》《属性名称》;
静态成员函数的声明方式如下:
static 《返回类型》《成员函数名称》(《参数列表》);
一般来讲,在静态函数中访问的基本上是静态数据成员或全局变量。
实例代码中的类定义了静态属性counter和静态函数表示目前的学生人数,更改student类的构造函数,id系统会根据counter自动分配。
#include <iostream.h>
class student
{
private:
static int counter;
int id;
public:
student();
void show();
static void setcounter(int);
};
int student::counter = 1;
student::student()
{
id = counter++;
}
void student::show()
{
cout<<id<<" ";
}
void student::setcounter(int new_counter)
{
counter = new_counter;
}
void main()
{
student s1;
s1.show();
student s2;
s2.show();
student s3;
s3.show();
s1.setcounter(10);
student s4;
s4.show();
student s5;
s5.show();
cout<<endl;
}
构建友元
友元从字面上来理解,就是“朋友成员”,友元提供了直接访问类的私有成员的方法。既可以将函数定义为友元,也可以将类本身定义为友元。友元函数就是将程序中的任意一个函数,甚至是另一个类定义的成员函数,声明为友元。该函数不是该类的成员函数,而是独立于类的外界的哈市,但是该函数可以访问这个类对象中的私有成员。定义格式如下:
friend《返回类型》《函数名》(《参数列表》);
除了友元函数外,一个类也可以被声明为另一个类的友元,该类被称为友元类。这就意味着作为友元的类中的所有成员函数都可以访问另一个类中的私有成员。声明格式如下:
friend class《类名》
访问修饰符
修饰符就是对类的成员的限定符,主要有const 和mutable两种。const表示不希望类的对象或类的属性在程序运行的过程中被修改:mutable表示总是可以修改。当把一个类声明为const时,它的所有成员函数属性都将自动成为const类型。但有时又需要修改某个const对象中的属性,这时就需要用到mutable修饰符。
演示const和mutable的使用方法。
#include <iosteam.h>
class A
{
int ax;
mutable int ay;
public:
A(int i,int j);
void show();
void show() const;
void modifyY(int y) const;
};
A::A(int i,int j)
{
ax=i;
ay=j;
}
void A::show()
{
cout<<"show()函数调用"<<endl;
cout<<ax<<endl;
cout<<ay<<endl;
}
void A::show() const
{
cout<<"void show() const 函数调用"<<endl;
cout<<ax<<endl;
cout<<ay<<endl;
}
void A::modifyY(int y) const
{
ay =y;
}
void main()
{
const A a1(5,10);
a1.show();
a1.modifyY(8);
a1.show();
A a2(10,15);
a2.show();
a2.modifyY(30);
a2.show();
}
分析:在上面程序中,在类A中声明了两个show()函数。根据创建对象的不同,系统会自动选择调用不同的函数。当调用了const a1对象的show()函数时,系统自动选择const成员函数。当调用非const a2对象时,系统自动选择非const成员函数。由于a1被定义为const型,因此必须将ay声明为mutable的,否则调用mudifyY函数修改ay的值将会报错,而调用a2的modifyY函数时,则不会。
指向类成员的指针
在c++中,可以定义一个指针,使其指向类成员或成员函数,然后通过指针来访问类的成员。这包括指向数据成员的指针和执行成员函数的指针
定义一个类stdent,该类有非静态成员math,静态成员chinese,代码演示了指向它们的指针定义方式。
student s1;//指向数据成员的指针
int student::*pm = &student::math; //指向非静态属性
s1.*pm =100; //设置非静态属性
int *pc = &student::chinese; //指向静态属性
*pc = 10; //指向静态属性
student s2;//指向成员函数的指针
float (student::*pf1)()= &student::f1; //指向非静态成员函数的指针
(s1.*pf1)(); //调用指向非静态成员函数的指针
void (*pf2)()= &student::f2; //指向静态成员函数的指针
pf2(); //调用静态成员函数
嵌套类
一个类可以在另一个类中定义。
class A
{
private:
class B
{
public:
......
};
};
另一种是在类的外部定义嵌套类
class A
{
private:
class B;
...
};
class B
{
public:
......
}
类文件的组织
信息隐藏对开发大型程序是非常有用的,它可以极大的保证程序的质量。类的用户对于类中具体如何实现的并不感兴趣,它只需要了解类的说明(其中包括类与外界的接口),这已足够使类的使用者能够使用类。一般,一个较大的类可以分为以下3个文件来存放。
- 将类的说明作为一个头文件来存放。
- 将类的实现单独组成一个文件,其中只包含关于类的成员函数的定义,可以单独编译,但因为没有入口点main()函数,所以不能运行。
- 将对类的使用放在一个文件中,其中包含一个main()函数。
类的具体表现–对象
对象是类的实例,它属于某个已知的类。因此定义对象之前,一定要先定义该对象的类。下面简单的介绍对象的定义。对象在确定了它的类以后,其定义格式如下:
《类名》《对象名表》
其中,类名是待定的对象所属的类的名字,及所定义的对象是该类的对象。
定义对象的几种方式:
student s1,s2; //普通对象
student *ps2; //对象指针
student student_array[10]; //对象数组
s1.math = 100; //对象属性
s1.setmath(100); //成员函数
ps2->math =90; //直接用指针访问成员
ps2->setmath(90);
(*ps2).math =90; //间接访问成员
(*ps2).setmath(90);
student_array[0].math = 100;
student_array[0].setmath(100);
指向自身的指针–this指针
this指针是指向调用成员函数的类对象的指针。在定义类对象时,每个类对象都会拥有一份独立的非静态的数据成员,而共享同一份成员函数的备份。显然,这样做的好处是可以节约存储空间。但是,在程序运行过程中,类对象是如何将成员函数绑定到属于自己的数据成员上的呢?完成这项绑定任务的就是this指针
演示this指针的使用方法:
void student::copy(student &s)
{
if(this == &s) //限制不能自身到自身复制
{
cout<<"不能复制自身!"<<endl;
return ;
}
else
{
id = s.id; <=>this->id = s.id;
name = s.name;
chinese = s.chinese;
english = s.english;
math = s.math;
}
}