#3:C++:类和对象的进一步讨论;
2018-02-06 07:23
323 查看
一、构造函数:
1. 一个对象的初始化:
在建立一个对象的时候,常常需要一些初始化的操作:赋初值,初始化等;
注:
不能在声明类类型的同时进行初始化:
class Time // 错误!!
{
hour = 0;
};
而且仅仅在数据为public的情况下可以在定义对象的时候赋初值!!!:
class Time
{... } time1 = { 13,45,23 };
两个值之间通过逗号分隔开;
2. 构造函数的说明:
1. 构造函数是一种特殊的函数,用户不需要亲自去调用它,而是在建立对象的同时自动执行!
2. 构造函数的名字必须和class 的名字相同,而不能由用户自己定义函数名;
3. 构造函数不具有任何返回值,不具有任何类型;
4. 用户需要自己定义函数体和函数参数变量表;
例: 类内定义构造函数:
class Time
{
private:
int hour;
int minute;
int sec;
public:
void SetTime();
void ShowTime();
Time()
{
hour = 0;
minute = 0;
sec = 0;
}
};
void Time :: SetTime()
{
cout << "Please Input the Time :" << endl;
cin >> hour >> minute >> sec;
}
void Time :: ShowTime()
{
cout << hour << ':' << minute << ':' << sec << endl;
}
int main()
{
Time time1;
time1.ShowTime();
time1. SetTime();
time1.ShowTime();
return 0;
} 在此例中,构造了一个构造函数,在建立对象的同时,自动执行这个函数,为各个变量赋初值;
例: 类外定义构造函数:
class Time
{
private:
int hour;
int minute;
int sec;
public:
void SetTime();
void ShowTime();
Time();
};
Time :: Time()
{
hour = 0;
minute = 0;
sec = 0;
}
void Time :: SetTime()
{
cout << "Please Input the Time :" << endl;
cin >> hour >> minute >> sec;
}
void Time :: ShowTime()
{
cout << hour << ':' << minute << ':' << sec << endl;
}
int main()
{
Time time1;
time1.ShowTime();
time1. SetTime();
time1.ShowTime();
return 0;
} 构造函数使用的几点说明:
1. 是在类对象进入作用域时,开始调用构造函数,即对象开始被使用时;
2. 构造函数没有返回值,所以在声明前加 void 也是错误的;
3. 构造函数不能被用户自己调用,必须是在成员生命周期开始时自己调用,且仅执行一次;
4. 如果用户自己没有定义构造函数,则系统会自动生成一个构造函数,但函数体为空;
带参数的构造函数:
一般格式 : 构造函数名(类型1 形参1,……)
注:
真正的实参是在定义对象的时候给出的,此时定义对象的格式:
class 类名 对象名(实参1,……);
例:
1. 带参构造函数中的形参,其对应实参在定义对象的时候给定;
2. 此时可以对不同的对象进行不同的初始化;
使用参数初始化表对数据成员初始化:
例:
Box :: Box(int l,int w,int h) : length(l),width(w),height(h){}// 后无分号;
即 在原来的函数首部加上冒号,然后列出初始化表;
构造函数的重载:
在同一个类中可以定义多个构造函数,而参数的类型或个数不同;
例:
第一个构造函数无参数: Box :: Box() { ... };
是在函数体中对私有变量进行赋值的;
第二个构造函数是直接在类体中定义的:
Box(int l,int w,int h): length(l),width(w),height(h) {}
有三个参数,是用参数初始化表对成员进行初始化;
说明:
1. 在调用时不必给出实参的构造函数,称为默认构造函数;
2. 当使用无参构造函数进行对象定义的时候,对象书写的语句应当正确:
Box box1;
而:
Box box1(); 是错误的,此时为声明一个普通函数其返回值为 Box型;
3. 在一个类中可以包含多个构造函数,但是系统只会执行一条;
使用默认参数的构造函数:
同之前的函数相同,构造函数也可以定义为含有默认参数的:
例:
实参和默认变量的结合是从左向右,所以应当把设为默认参数的形参放在函数声明的末尾;
析构函数:
析构函数也是一个特殊的成员函数,作用与构造函数相反,函数名为类名前加 ~ ;
当对象的生命期结束的时候,系统将自动执行析构函数:
情况有:
1. 在某个函数中定义了一个对象(自动全局变量),当这个函数被调用时,对象应当释放,在释放前执行;
2. static 型变量在函数结束时也不释放,而是在main函数 或执行exit函数结束时,才调用函数;
3. 对于一个全局变量,在函数的流程超过其作用域的时候(main exit 结束时),调用析构函数;
4. 用 new 动态分配建立一个对象后,当用delete删除时,首先调用析构函数;
注:
1. 析构函数的作用不是释放空间,而是在释放空间前,执行一些清理工作;
2. 析构函数不返还任何值,没有函数类型,也没有函数参量,所以也不能被重载;
所以一个类可以有多个构造函数,但是只能有一个析构函数;
3. 析构函数其实是执行:用户希望在最后一次使用对象之后所执行的任何操作(输出信息等);
4. 如果用户没有定义析构函数,同样的,系统也会自动生成一个空函数体的析构函数;
例:
先构造的后析构,后构造的先析构!!!!!!!!( 在同一个函数中定义的局部变量,作用域相同 )
1. 在全局中定义的对象(所有函数之外),他的构造函数在所有函数执行之前被调用;
注:对于一个程序中多个文件中都定义了全局变量,此时构造函数的调用是不确定的;
当main 或 exit 执行后,调用析构函数;
2. 如果定义的是局部变量(在某函数中),则在每一次建立对象时调用其构造函数。
如果函数被多次调用,则每一次建立对象时都需要调用构造函数;
当函数调用结束、对象释放时,先调用析构函数;
3. 如果在函数中定义静态static局部对象,则只在程序第一次调用此函数时调用构造函数;
在函数调用结束时,对象并不释放,也不调用析构函数;
当main 或 exit 执行后,调用析构函数;
例:
void fn()
{
Student stu1;
static Student stu2;
}
在调用 fn 函数时,先调用 stu1的构造函数,在执行stu2的构造函数;
在 fn 调用结束时,stu1 需要释放,此时调用 stu1的析构函数;
而stu2 是静态局部变量,只有在 main 结束时才释放,此时调用 stu2 的析构函数;
对象数组:
1. 定义: Student stud[50];
2. 构造函数如果只有一个参数:
例:
Student :: Student(int num);
此时可以直接在等号后面提供实参:
Student stud[3] = {60,61,62};
3. 构造函数有多个参数:
例:
Student :: Student(int ,int ,int );
此时,3个参数分别为三个元素的第一个实参,即:
编译系统只会为每一个对象元素的构造函数提供一个实参!!!
此时可以定义:
Student stud[3] = {
Student(1001,18,88);
Student(1002,11,22);
Student(1003,13,33);
};
即在建立对象数组时,分别调用构造函数,对每个元素初始化;
例:
对象指针:
一、指向对象的指针:
在建立对象时,编译系统会为每一个变量分配空间,而对象空间的起始地址就是变量名;
例:
Time *pt;
Time t1;
pt = &t1; // 将 Time 类型的变量 t1 的起始地址作为指针给 pt;
此时可以通过指针访问变量(public)
(*pt).hour
pt->hour
(*pt).get_time();
pt->get_time();
二、指向对象成员的指针:
1. 指向对象数据成员的指针:
此时和一般的数据指针的定义相同:
int *pt = &t1.hour;
cout << *pt << endl;
2. 指向对象成员函数的指针:
指向普通函数的指针变量的方法:
数据类型名 (* 指针变量名) (参数表列);
例:
void (* p)(); // p 是指向void型函数的指针变量;
此时可以使他指向一个函数:
p = fun;
(*p)() ; // 调用 fun 函数;
指向对象成员函数的指针:
注: p = t1.get_time; // 错误,编译系统要求赋值语句中左右两侧的类型相匹配;
而此时 p 为普通指针与 Time 类型无关,但是 get_time() 为 Time 型;
所以要求在以下三个方面都匹配:
1. 函数参数的类型和参数个数;
2. 函数返回值的类型;
3. 所属的类;
应当区别普通函数和成员函数的不同性质:
void (Time :: *p2)(); // 定义一个指向 Time 类 返回值为 void 公用成员函数的指针;
p2 = &Time :: get_time;
注:
定义括号不可省略:
否则成为: void Time :: *(p2()); // 返回值为 void 型指针的函数;
定义的一般形式为:
数据类型名 (类名 :: * 指针变量名) ( 参数表列 )
使指针变量指向一个公用成员函数的形式为:
指针变量名 = & 类名 :: 成员函数名;
例:
注: 成员函数是存放在对象空间外的空间中的,多个同类对象共用一个代码段,
所以赋给指针的应当是公用函数代码段的入口地址;
this
指针:
对于 n 个不同的对象,系统应当分配 n 个不同的空间,但是,他们共有相同的函数段;
那么,当不同对象的成员函数调用函数是,是怎么保证引用的是所指定的成员数据的呢?
其实在每一个成员函数中包含着一个特殊的指针 this :
他是指向本类的指针,它的值就是当前被调用的成员函数所对应的对象的起始地址;
所以,当系统调用如:a.volume 函数时:
编译系统就会把 a 的起始地址给 this ,此时this 找到相应的成员函数;
实际上是执行 : (this -> length)*(this -> width)*(this -> height);
当调用 b.volume 时,编译系统又将 b 的起始地址给 this 完成操作;
注:
this 指针是作为隐式来使用的,它是作为参数被传递给成员函数的:
对于函数:
int Box :: volume()
{
return (length)*(width)*(height);
}
在 c++ 中被处理为:
int Box :: volume(Box *this)
{
return (this -> length)*(this -> width)*(this -> height);
}
而在调用时,实际上是:
a.volume(&a); 从而将 a 的地址传给 this ;
在使用时,也可以使用显式的this ,此时指代的是当前对象;
1. 一个对象的初始化:
在建立一个对象的时候,常常需要一些初始化的操作:赋初值,初始化等;
注:
不能在声明类类型的同时进行初始化:
class Time // 错误!!
{
hour = 0;
};
而且仅仅在数据为public的情况下可以在定义对象的时候赋初值!!!:
class Time
{... } time1 = { 13,45,23 };
两个值之间通过逗号分隔开;
2. 构造函数的说明:
1. 构造函数是一种特殊的函数,用户不需要亲自去调用它,而是在建立对象的同时自动执行!
2. 构造函数的名字必须和class 的名字相同,而不能由用户自己定义函数名;
3. 构造函数不具有任何返回值,不具有任何类型;
4. 用户需要自己定义函数体和函数参数变量表;
例: 类内定义构造函数:
class Time
{
private:
int hour;
int minute;
int sec;
public:
void SetTime();
void ShowTime();
Time()
{
hour = 0;
minute = 0;
sec = 0;
}
};
void Time :: SetTime()
{
cout << "Please Input the Time :" << endl;
cin >> hour >> minute >> sec;
}
void Time :: ShowTime()
{
cout << hour << ':' << minute << ':' << sec << endl;
}
int main()
{
Time time1;
time1.ShowTime();
time1. SetTime();
time1.ShowTime();
return 0;
} 在此例中,构造了一个构造函数,在建立对象的同时,自动执行这个函数,为各个变量赋初值;
例: 类外定义构造函数:
class Time
{
private:
int hour;
int minute;
int sec;
public:
void SetTime();
void ShowTime();
Time();
};
Time :: Time()
{
hour = 0;
minute = 0;
sec = 0;
}
void Time :: SetTime()
{
cout << "Please Input the Time :" << endl;
cin >> hour >> minute >> sec;
}
void Time :: ShowTime()
{
cout << hour << ':' << minute << ':' << sec << endl;
}
int main()
{
Time time1;
time1.ShowTime();
time1. SetTime();
time1.ShowTime();
return 0;
} 构造函数使用的几点说明:
1. 是在类对象进入作用域时,开始调用构造函数,即对象开始被使用时;
2. 构造函数没有返回值,所以在声明前加 void 也是错误的;
3. 构造函数不能被用户自己调用,必须是在成员生命周期开始时自己调用,且仅执行一次;
4. 如果用户自己没有定义构造函数,则系统会自动生成一个构造函数,但函数体为空;
带参数的构造函数:
一般格式 : 构造函数名(类型1 形参1,……)
注:
真正的实参是在定义对象的时候给出的,此时定义对象的格式:
class 类名 对象名(实参1,……);
例:
class Box { public: Box(int ,int ,int ); int volume(); private: int length; int width; int height; }; Box :: Box(int l,int w,int h) { length = l; width = w; height = h; } int Box :: volume() { return (length * width * height); } int main() { Box box1(12,25,20); cout << "The volume of this box is :" << box1.volume() << endl; Box box2(15,30,21); cout << "The volume of this box is :" << box2.volume() << endl; return 0; }说明:
1. 带参构造函数中的形参,其对应实参在定义对象的时候给定;
2. 此时可以对不同的对象进行不同的初始化;
使用参数初始化表对数据成员初始化:
例:
Box :: Box(int l,int w,int h) : length(l),width(w),height(h){}// 后无分号;
即 在原来的函数首部加上冒号,然后列出初始化表;
构造函数的重载:
在同一个类中可以定义多个构造函数,而参数的类型或个数不同;
例:
class Box { public: Box(); Box(int l,int w,int h): length(l),width(w),height(h) {} int volume(); private: int length; int width; int height; }; Box :: Box() { length = 10; width = 10; height = 10; } int Box :: volume() { return (length * width * height); } int main() { Box box1; cout << "The volume of this box is :" << box1.volume() << endl; Box box2(15,30,21); cout << "The volume of this box is :" << box2.volume() << endl; return 0; }在此类中定义了两个构造函数:
第一个构造函数无参数: Box :: Box() { ... };
是在函数体中对私有变量进行赋值的;
第二个构造函数是直接在类体中定义的:
Box(int l,int w,int h): length(l),width(w),height(h) {}
有三个参数,是用参数初始化表对成员进行初始化;
说明:
1. 在调用时不必给出实参的构造函数,称为默认构造函数;
2. 当使用无参构造函数进行对象定义的时候,对象书写的语句应当正确:
Box box1;
而:
Box box1(); 是错误的,此时为声明一个普通函数其返回值为 Box型;
3. 在一个类中可以包含多个构造函数,但是系统只会执行一条;
使用默认参数的构造函数:
同之前的函数相同,构造函数也可以定义为含有默认参数的:
例:
class Box { public: Box(int l = 10,int w = 10,int h = 10); // 使用默认参数; int volume(); private: int length; int width; int height; }; Box :: Box(int l,int w,int h): length(l),width(w),height(h) {}; int Box :: volume() { return (length * width * height); } int main() { Box box1; cout << "The volume of this box is :" << box1.volume() << endl; Box box2(15); cout << "The volume of this box is :" << box2.volume() << endl; Box box3(15,30); cout << "The volume of this box is :" << box3.volume() << endl; Box box4(15,30,20); cout << "The volume of this box is :" << box4.volume() << endl; return 0; }注:
实参和默认变量的结合是从左向右,所以应当把设为默认参数的形参放在函数声明的末尾;
析构函数:
析构函数也是一个特殊的成员函数,作用与构造函数相反,函数名为类名前加 ~ ;
当对象的生命期结束的时候,系统将自动执行析构函数:
情况有:
1. 在某个函数中定义了一个对象(自动全局变量),当这个函数被调用时,对象应当释放,在释放前执行;
2. static 型变量在函数结束时也不释放,而是在main函数 或执行exit函数结束时,才调用函数;
3. 对于一个全局变量,在函数的流程超过其作用域的时候(main exit 结束时),调用析构函数;
4. 用 new 动态分配建立一个对象后,当用delete删除时,首先调用析构函数;
注:
1. 析构函数的作用不是释放空间,而是在释放空间前,执行一些清理工作;
2. 析构函数不返还任何值,没有函数类型,也没有函数参量,所以也不能被重载;
所以一个类可以有多个构造函数,但是只能有一个析构函数;
3. 析构函数其实是执行:用户希望在最后一次使用对象之后所执行的任何操作(输出信息等);
4. 如果用户没有定义析构函数,同样的,系统也会自动生成一个空函数体的析构函数;
例:
class Student { public: Student(int ,string ,char ); // 构造函数 ~Student(); // 析构函数 void Display(); private: int num; string name; char sex; }; Student :: Student(int xuehao,string ming,char xingbie) { num = xuehao; name = ming; sex = xingbie; cout << "The constructor is called !" << endl; } Student :: ~Student() { cout << "The Disconstructor is called !" << num <<endl; } void Student :: Display() { cout << "The number is :" << num << endl; cout << "The name is :" << name << endl; cout << "The sex is :" << sex << endl; } int main() { Student stu1(100,"ZK",'M'); stu1.Display(); Student stu2(101,"ZKK",'M'); stu2.Display(); return 0; }注: 调用构造函数和析构函数的顺序:
先构造的后析构,后构造的先析构!!!!!!!!( 在同一个函数中定义的局部变量,作用域相同 )
1. 在全局中定义的对象(所有函数之外),他的构造函数在所有函数执行之前被调用;
注:对于一个程序中多个文件中都定义了全局变量,此时构造函数的调用是不确定的;
当main 或 exit 执行后,调用析构函数;
2. 如果定义的是局部变量(在某函数中),则在每一次建立对象时调用其构造函数。
如果函数被多次调用,则每一次建立对象时都需要调用构造函数;
当函数调用结束、对象释放时,先调用析构函数;
3. 如果在函数中定义静态static局部对象,则只在程序第一次调用此函数时调用构造函数;
在函数调用结束时,对象并不释放,也不调用析构函数;
当main 或 exit 执行后,调用析构函数;
例:
void fn()
{
Student stu1;
static Student stu2;
}
在调用 fn 函数时,先调用 stu1的构造函数,在执行stu2的构造函数;
在 fn 调用结束时,stu1 需要释放,此时调用 stu1的析构函数;
而stu2 是静态局部变量,只有在 main 结束时才释放,此时调用 stu2 的析构函数;
对象数组:
1. 定义: Student stud[50];
2. 构造函数如果只有一个参数:
例:
Student :: Student(int num);
此时可以直接在等号后面提供实参:
Student stud[3] = {60,61,62};
3. 构造函数有多个参数:
例:
Student :: Student(int ,int ,int );
此时,3个参数分别为三个元素的第一个实参,即:
编译系统只会为每一个对象元素的构造函数提供一个实参!!!
此时可以定义:
Student stud[3] = {
Student(1001,18,88);
Student(1002,11,22);
Student(1003,13,33);
};
即在建立对象数组时,分别调用构造函数,对每个元素初始化;
例:
class Box { public: Box(int l,int w,int h) : length(l),width(w),height(h){} int volume(); private: int length; int width; int height; }; int Box :: volume() { return (length * width * height); } int main() { Box box[3] = { Box(10,10,10), Box(10,10,15), Box(10,15,10), }; for(int i = 0; i < 3 ; i++) { cout << "The volume of the box " << i << " is " << box[i].volume() << endl; } cout << endl; return 0; }
对象指针:
一、指向对象的指针:
在建立对象时,编译系统会为每一个变量分配空间,而对象空间的起始地址就是变量名;
例:
Time *pt;
Time t1;
pt = &t1; // 将 Time 类型的变量 t1 的起始地址作为指针给 pt;
此时可以通过指针访问变量(public)
(*pt).hour
pt->hour
(*pt).get_time();
pt->get_time();
二、指向对象成员的指针:
1. 指向对象数据成员的指针:
此时和一般的数据指针的定义相同:
int *pt = &t1.hour;
cout << *pt << endl;
2. 指向对象成员函数的指针:
指向普通函数的指针变量的方法:
数据类型名 (* 指针变量名) (参数表列);
例:
void (* p)(); // p 是指向void型函数的指针变量;
此时可以使他指向一个函数:
p = fun;
(*p)() ; // 调用 fun 函数;
指向对象成员函数的指针:
注: p = t1.get_time; // 错误,编译系统要求赋值语句中左右两侧的类型相匹配;
而此时 p 为普通指针与 Time 类型无关,但是 get_time() 为 Time 型;
所以要求在以下三个方面都匹配:
1. 函数参数的类型和参数个数;
2. 函数返回值的类型;
3. 所属的类;
应当区别普通函数和成员函数的不同性质:
void (Time :: *p2)(); // 定义一个指向 Time 类 返回值为 void 公用成员函数的指针;
p2 = &Time :: get_time;
注:
定义括号不可省略:
否则成为: void Time :: *(p2()); // 返回值为 void 型指针的函数;
定义的一般形式为:
数据类型名 (类名 :: * 指针变量名) ( 参数表列 )
使指针变量指向一个公用成员函数的形式为:
指针变量名 = & 类名 :: 成员函数名;
例:
class Time { public: Time(int ,int ,int ); int hour; int minute; int sec; void ShowTime(); }; Time :: Time(int h,int m,int s) { hour = h; minute = m; sec = s; } void Time :: ShowTime() { cout << "The time is : " << hour << " : " << minute << " : " << sec <<endl; } int main() { Time t1(12,14,30); int *p1 = &t1.hour; cout << "The hour is : " << *p1 << endl; // 定义一个指向 int 成员的普通指针; t1.ShowTime(); // 成员调用函数; Time *p2 = &t1; p2->ShowTime(); // 定义一个指向对象的指针,并用指针指向成员函数 // void (Time :: *p3) () = &ShowTime; void (Time :: *p3) (); // 定义一个指向成员函数的指针; p3 = &Time :: ShowTime; (t1.*p3) (); // 用成员符号调用函数指针; return 0; }
注: 成员函数是存放在对象空间外的空间中的,多个同类对象共用一个代码段,
所以赋给指针的应当是公用函数代码段的入口地址;
this
指针:
对于 n 个不同的对象,系统应当分配 n 个不同的空间,但是,他们共有相同的函数段;
那么,当不同对象的成员函数调用函数是,是怎么保证引用的是所指定的成员数据的呢?
其实在每一个成员函数中包含着一个特殊的指针 this :
他是指向本类的指针,它的值就是当前被调用的成员函数所对应的对象的起始地址;
所以,当系统调用如:a.volume 函数时:
编译系统就会把 a 的起始地址给 this ,此时this 找到相应的成员函数;
实际上是执行 : (this -> length)*(this -> width)*(this -> height);
当调用 b.volume 时,编译系统又将 b 的起始地址给 this 完成操作;
注:
this 指针是作为隐式来使用的,它是作为参数被传递给成员函数的:
对于函数:
int Box :: volume()
{
return (length)*(width)*(height);
}
在 c++ 中被处理为:
int Box :: volume(Box *this)
{
return (this -> length)*(this -> width)*(this -> height);
}
而在调用时,实际上是:
a.volume(&a); 从而将 a 的地址传给 this ;
在使用时,也可以使用显式的this ,此时指代的是当前对象;
相关文章推荐
- 谭浩强版C++课本实例 第三章 关于类和对象的进一步讨论(14)
- 谭浩强C++程序设计第9章关于类和对象的进一步讨论
- 关于类和对象的进一步讨论 C++
- C++ 第九章 关于类和对象的进一步讨论(二)const的用法和指针数据成员
- 关于类和对象的进一步讨论之析构函数 C++
- 【深入探索c++对象模型】类对象所需内存大小讨论续写
- 【怎样写代码】复杂对象的组装与创建 -- 建造者模式(五):关于Director的进一步讨论
- C++对象大小讨论
- 关于类和对象的进一步讨论
- C++第九章 类和对象的进一步认识(一)
- C++虚拟多重继承对象模型讨论
- 第四章 类和对象的进一步讨论-续
- C++虚拟多重继承对象模型讨论
- 【深入探索c++对象模型】c++中构造函数调用虚函数的讨论
- C++类与对象的进一步讨论(1)
- C++类与对象的进一步讨论(2)
- 【深入探索c++对象模型】类对象所需内存大小讨论
- c++对象和内存 对虚继承的讨论
- C++程序设计-第4周 和对象进一步相处
- 第四章 类和对象的进一步讨论