赞
踩
在对类对象的数据进行赋值时,可以在类中直接赋值,但不推荐,因为类不是实体,是抽象的概念,不占用存储空间。
class Student
{
public:
string addr = "北京市朝阳区";
void display();
private:
int num = 0;
string name = "Tom";
char sex = 'M';
};
也可以用成员函数赋值。
void Student::SetStudent() {
num = 1;
name = "Jack";
sex = 'M';
addr = "北京市昌平区";
}
当类中的数据成员较多时,上面两种方法显然效率不高。C++在类中提供了构造函数来对类对象进行初始化。
(1)无参构造函数
在C++中, 构造函数与类同名
,是一种特殊的成员函数,与其他成员函数不同,它是在 建立对象时自动调用的
,不需要用户来调用。
// 在类内
Student(){
num = 2;
name = "Merry";
sex = 'F';
addr = "北京市西城区";
}
// 在类外
Student::Student(){
num = 2;
name = "Merry";
sex = 'F';
addr = "北京市西城区";
}
注意
:
<1>构造函数只是对类对象进行初始化,不需要声明类型;
<2>构造函数在创建对象时,由系统自动调用。每创建一次就调用一次。
<3>如果用户没有定义,系统会自动生成一个没有参数,没有函数体,不执行初始化操作的构造函数。
(2)带参构造函数
<1>默认参数构造函数
在建立对象时,不用给出实参的构造函数,称为 默认构造函数
。
类中声明:
Student(int n = 4, string name_ = "Jobs", char s = 'M', string address = "美国");
类外定义:
Student::Student(int n, string name_, char s, string address)
{
num = n;
name = name_;
sex = s;
addr = address;
}
主函数调用:
Student stud;
注意
:
无参构造函数也属于默认构造函数; 一个类只能有一个默认构造函数
。
<2>非默认参数构造函数
对不同对象初始化不同的初值。
类中声明:
Student(int, string, char, string);
类外实现:
Student::Student(int number, string name_, char sex_, string address){
num = number;
name = name_;
sex = sex_;
addr = address;
}
主函数中调用:
Student stud1(3, "God", 'M', "上海市");
注意
:在类中声明带参构造函数时,形参可以只有类型,无形参名。
(3)参数初始化表
参数初始化表减少函数体长度,使得函数结构精简。
Student(int n, string name_, char s, string address, int score[]):num(n), name(name_), sex(s), addr(address)
{
for(int i = 0;i < 12;i++)
{
Score[i] = score[i];
}
}
注意
:若果数据成员是数组,则应该在构造函数的函数体中对其进行初始化,而不能在参数初始化表中对其进行初始化。
(4)构造函数的重载
在类中可以声明多个构造函数,包括无参构造函数和带参构造函数。
类中声明: Student(); Student(int, string, char, string); 类外定义: Student::Student() {} Student::Student(int n, string name_, char s, string address) { num = n; name = name_; sex = s; addr = address; } 主函数中调用: Student stud; stud.display(); Student stud1(3, "God", 'M', "上海市"); stud1.display();
或 使用参数初始化表。
类内声明:
Student();
Student(int n, string name_, char s, string address):num(n), name(name_), sex(s), addr(address){ }
类外定义:
Student::Student() {}
主函数中调用:
Student stud;
stud.display();
Student stud1(3, "God", 'M', "上海市");
stud1.display();
注意
:参数初始化表不需要类外定义。
(5)析构函数
析构函数(destructor)也是一种特殊的成员函数,它的作用与构造函数相反,在类名前加一个“~”符号。作用是 在撤销对象占用的内存之前完成一些清理工作
,使得这部分内存可以被重新利用。
当对象生命周期结束时,自动执行析构函数。
~Student()
{
cout<<"deconstructed!!!"<<endl;
}
注意
:
<1>函数中的局部对象释放前,自动执行析构函数;
<2>静态(static)局部对象在函数调用结束时对象并不释放,在main函数结束或调用exit时执行析构函数;
<3>全局对象在main函数结束或调用exit时执行析构函数;
<4>用new运算符动态建立的对象,使用delete运算符释放时,调用析构函数;
<5>一个类可以有多个构造函数,但只能有一个析构函数。
构造函数和析构函数的调用顺序遵循:先构造的后析构,后构造的先析构
。
对象数组的每一个元素都是同类的对象
。
Student student[3] = {
Student(),
Student(3, "God", 'M', "加拿大"),
Student(4, "Med", 'M', "美国"),
};
(1)指向对象的指针
一个对象的存储空间的起始地址就是对象的指针
,定义一个指针变量指向起始地址,就是指向对象的指针变量。
Student *p, s1;
p = &s1;
p->display();
(*p).display();
(2)指向对象成员的指针
对象成员也有地址, 存放对象成员地址的指针变量
就是指向对象成员的指针变量。
指向对象数据成员的指针 Student s; string *p; p = &s.addr; cout<<*p<<endl; 指向对象成员函数的指针 void(Student::*p)(); p = &Student::display; Student s; (s.*p)(); 等价于: void(Student::*p1)() = &Student::display; Student s; (s.*p)();
附带:
指向普通函数的指针变量
void(*p)();
p = fun;
(*p)();
(3)指向当前对象的this指针
this指针是隐式的,系统自带的。
调用对象a的成员函数f:实际上是调用成员函数f时使this指针指向对象a,从而访问对象a的成员
。
(1)常对象
关键词const修饰,常对象必须要有初值。
Student const s1(1, "小明", 'M', "安徽");
const Student s2(2, "小狗", 'M', "香港");
如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数,而不能调用该对象的普通成员函数(构造函数和析构函数除外,二者还是会由系统自动调用的)。
(2)常数据成员
const int age;
只能通过 构造函数的参数初始化表对常数据成员进行初始化
,其他函数均不能。
常对象的数据成员都是常成员
。
(3)常成员函数
常成员函数只能引用本类中的数据成员,不能修改。
类内声明:
void display() const;
类外定义:
void Student::display() const{
cout<<"number:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"address:"<<addr<<endl;
}
主函数中调用:
Student s(1, 12, "小明", 'M', "安徽");
s.display();
(4)指向对象的常指针
const型指针变量的值不能修改
。因此,指向对象的常指针,即 一个常指针指向了一个对象
,其值也不能改变,始终指向同一个对象,但可以改变对象的值。
Student s(1, 12, "小明", 'M', "安徽");
Student * const p = &s;
p->age;
p->display();
(5)指向常对象的指针变量
常对象:const 类名 对象名
定义指向常变量的指针变量的一般形式为:const 类型名 *指针变量名
const student stud;
const student *p;
p = &stud;
注意
:
(1)如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它
,而不能用一般指针变量(非const类型)。
(2)指向常变量的指针变量 指向未被声明为const的变量
时,不能通过该指针变量修改该变量的值
。
(3)如果函数形参是指向非const变量的指针变量,则实参只能是指向非const变量的指针。形参若是指向const型变量的指针,实参既可以是指向const变量的指针,也可以是指向非const变量的指针。
指向常对象的指针常用于函数的形参,以保护形参指针所指向对象不被修改。
规则
:
当希望在调用函数时对象的值不被修改,应当把形参定义为指向常对象的指针变量,同时用对象的地址做实参(对象可以是const或非const)。
如果希望对象的值不仅在调用函数时不被修改,在程序执行过程中也不修改,应当把对象声明为const型。
(6)对象的常引用
一个变量的引用就是一个变量的别名。实质上,变量名和引用名都指向同一段内存单元。
一般引用:由于 引用作为形参
,所以可以修改对应实参的值。
函数定义:
void fun(student &stud)
{
stud.age = 20;
}
在main函数中:
student s(1, 12, "小明", 'M', "安徽");
fun(s);
常引用:形参为常引用时,不能修改对应实参的值。
函数定义:
void fun(const student &stud)
{
stud.age = 20;
}
在main函数中:
student s(1, 12, "小明", 'M', "安徽");
fun(s);
注意
:在C++中,经常使用 常指针和常引用
作为函数参数。这样既能使数据不被随意修改,保证数据安全,在调用函数时又不必简历实参的拷贝。在每次调用函数建立实参的拷贝时,都要调用复制构造函数,要有时间开销
。用常指针和常引用作为函数参数,能 提高程序运行效率
。
const小结:
形式 | 含义 |
---|---|
student const stud; | stud是 常对象 ,其值任何时候都不能修改。 |
void student::fun() const; | fun是student类的 常成员函数 ,可以引用但不能修改本类的成员数据。 |
student * const p; | p是 指向对象的常指针变量 ,p的值(p的指向)不能被修改(指向不能修改但是可改变存储空间的值)。 |
const student *p; | p是 指向 student类 常对象的指针变量 ,p指向的类对象的值不能通过p修改。 |
const student &stud1 = stud; | stud1是stud的 引用或别名 ,二者共享同意存储空间,stud的值不能被修改。 |
“静态”可以实现数据共享
。这里注意,const是用来保护数据的。
大家知道,全局变量可以实现数据共享,但是由于全局变量即可以被访问,也能被随意修改,所以全局变量安全性得不到保障,故实际工作中很少使用全局变量。
如果想 在同类中多个对象之间实现数据共享
,可以用静态的数据成员,而不是用全局对象。
(1)静态数据成员
一般形式:
class student
{
public:
static int age;
void display();
private:
static string provi;
};
注意
:
<1>静态数据成员是在所有对象之外单独开辟空间,它不属于类对象
。一般声明类而未定义时,则类的数据成员不占用内存,只有在定义时才为数据成员分配空间。静态数据成员声明时即分配空间;
<2>静态数据成员是在 程序编译时被分配空间
,到 程序结束时才释放空间
,一般数据成员在对象建立时分配空间,对象撤销时释放;
<3>静态数据成员 只能在类体外进行初始化
,不能用参数初始化表对其初始化。如果未初始化,则编译系统自动赋予初值0;
<4>静态数据成员即可以通过对象名引用,也可以通过类名来引用。若静态数据成员为私有的,则不能直接在类外引用,必须通过公用的成员函数引用。
class student { public: static int age; void display(); private: static string provi; }; void student::display() { ... } int student::age = 18; string student::provi = "北京"; //注意,这里是初始化,所以在类外可以对私有静态数据成员进行初始化,但是不能在main函数中引用。 在main函数中: cout<<student::provi<<endl;//这是错误的,provi是私有的。
(2)静态成员函数
static void display();
<1>静态成员函数是类的一部分而不是对象的一部分
。如果要在类外调用公用的静态成员函数,要用类名和域运算符“::”。
student::display();
或
student stud;
stud.display();
允许通过对象名调用静态成员函数,但静态成员函数不属于类对象。
<2>静态成员函数没有this指针,所以静态成员函数不能访问本类的非静态数据成员
,前面提到:当调用一个对象的成员函数时,系统会把该对象的起始地址赋给成员函数的this指针。而静态成员函数不属于类对象,它与任何类对象都无关。
<3>在C++中,静态成员函数主要用来访问静态成员数据,而不访问非静态成员数据。应该养成好习惯:只用静态成员函数引用静态数据成员,而不引用非静态数据成员
。
注意区别const和static:const用来保护数据,static是为了共享数据。
new动态建立对象,delete撤销对象。
student *p;
p = new student;
注意
:用new运算符动态地分配内存后,将返回一个指向新对象的指针,即所分配的内存空间的起始地址。可 定义一个指向本类对象的指针变量来存放该地址
。
(1)对象赋值
同类对象
之间可以相互对 数据成员
进行赋值。
student stud,stud1;
stud = stud1;
(2)对象复制
用已有的对象stud克隆一个stud1,背后是通过调用 复制构造函数
实现的。
对象复制一般形式:
student stud1(stud);
或
student stud1 = stud;
复制构造函数:
student::student(const student &stud)
{
num = stud.num;
age = stud.age;
...
}
注意
:
<1>对象赋值是已存在一个对象,然后对其赋值;对象复制是创建一个和已有对象完全一样的新对象。
<2>复制构造函数
一般形式:
类名(类名 &对象名);
student(student &stud);
普通构造函数在建立对象时调用,复制构造函数在复制对象时调用。
复制构造函数的调用(全为系统自动实现,了解即可):
友元(friend)包括友元函数和友元类。友元用来访问与其有友好关系的类中的私有成员
。
(1)将普通函数声明为友元函数
class student { public: int age; friend void display(student &); private: string provi; }; //void student::display(student &s)//类student的成员函数display定义 void display(student &s)//display函数不是类student的成员函数 { cout<<s.age<<endl; cout<<s.provi<<endl; }
注意:display函数不是类student的成员函数,没有this指针,不能默认引用student类的数据成员,必须指定要访问的对象。
(2)友元成员函数
友元函数还可以是另一个类中的成员函数。
class Date;
class Time
{
public:
void display(Date &);
...
}
class Date
{
public:
friend void Time::display(Date &);
}
注意:
<1>一个函数(包括普通函数和成员函数)可以被多个类声明为“朋友”,这样就可以引用多个类中的私有数据
;
<2>C++允许对类作“提前引用”的声明,即在正式声明一个类之前,先声明一个类名,表示此类在稍后声明。
(3)友元类
在类A的定义体中,声明类B为其友元类。
class B;
class A
{
friend B;
}
友元类B的所有函数都是类A的友元函数,可以访问类A中的所有成员。
注意
:
<1>友元关系是单向而不是双向;
<2>友元的关系不能传递。
有两个或多个类,其功能是相同的,仅仅是数据类型不同
。
template <class numtype> class Compare { public: Compare(numtype a, numtype b) { x = a; y = b } numtype max() { return (x>y)?x:y; } numtype min() { return (x<y)?x:y; } private: numtype x,y; }
类模板是类的抽象,类是类模板的实例。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。