以下为个人学习笔记和习题整理
课程:计算机程序设计(C++)- 西安交通大学 @ 中国大学 MOOC
https://www.icourse163.org/course/XJTU-46006
# 课堂笔记
# 构造函数
在声明一个对象后,紧接着要给对象初始化,对象初始化实质上就是对所有数据成员赋值。
如果对象中某个数据成员未赋值,则该数据成员的值不确定,那么该对象的值不完整。
构造函数 Constructor
用于创建一个对象,提供了初始化该对象的一种简便手段。
<类名> (<参数表>) | |
{ | |
<函数体> | |
} |
注意:
- 构造函数的函数名必须与类名相同
- 构造函数没有返回值
- 其功能是将对象中的所有数据成员进行初始化,一般由一系列赋值语句构成
- 由系统在声明对象时自动调用
# 有无构造函数的区别
在前面章节中的程序声明变量
int a=0; | |
int a(0); | |
struct Date today={2013,3,19}; | |
struct Date today(2013,3,19); |
以上为变量声明的同时赋初值,即都允许初始化。
- 对于无构造函数的类,声明变量(对象)不允许初始化
CLOCK alarm={10,53,11}; // 错误 | |
CLOCK alarm(10,53,11); // 错误 |
- 有构造函数就允许初始化
Date birthday(1998,12,12); // 正确 | |
Date birthday={1998,12,12}; // 错误 |
# 重载构造函数
- 一个类中允许定义多个构造函数
- 在声明对象时自动选择执行某个构造函数
数据成员初始化的四种方法:
- 在构造函数的函数体中进行初始化
Date(Date &d) | |
{ | |
year=d.year; | |
month=d.month; | |
day=d.day; | |
} |
- 在构造函数的头部初始化
<类名>::<构造函数名>(<参数表>):<变量1>(<初值1>),…<变量n>(<初值n>){…} |
Date::Date():year(1900),month(1),day(1) | |
{ | |
} |
- 混合初始化:前两种方式结合
Date::Date(int y,int m,int d):year(y),month(m) | |
{ | |
day=d; | |
} |
- 使用默认参数初始化
Date(char yy[], int mm = 1, int dd = 1) | |
{ | |
year = atoi(yy); | |
month = mm; | |
day = dd; | |
} |
# 例子:日期类中 4 个重载构造函数
#include<iostream> | |
#include<string.h> | |
#include<stdlib.h> | |
using namespace std; | |
class Date | |
{ | |
int year,month,day; | |
public: | |
// 无参构造函数 | |
Date():year(1900),month(1),day(1) { } | |
// 默认参数构造函数 | |
Date(int yy,int mm = 1,int dd = 1); | |
// 日期对象参数构造函数 | |
Date(Date &d):year(d.year),month(d.month),day(d.day){}; | |
// 字符串日期构造函数 | |
Date(char *ps); | |
void print_ymd(); | |
}; | |
// 默认参数构造函数的定义 | |
Date::Date(int yy,int mm,int dd):year(1900),month(1),day(1) | |
{ | |
if (yy>=1900&&yy<=9999) | |
{ | |
year = yy; | |
} | |
else | |
{ | |
return; | |
} | |
if (mm>=1&&mm<=12) | |
{ | |
month = mm; | |
} | |
else | |
{ | |
year=1900; | |
return; | |
} | |
if (dd>=1&&dd<=31) | |
{ | |
day = dd; | |
} | |
else | |
{ | |
year=1900; | |
month=1; | |
return; | |
} | |
}; | |
// 重载构造函数的定义 | |
Date::Date(char *ps):year(1900),month(1),day(1) | |
{ | |
char py[5],pm[3],pd[3]; | |
strncpy(py,ps,4); | |
ps=ps+5; | |
strncpy(pm,ps,2); | |
ps=ps+3; | |
strncpy(pd,ps,2); | |
int yy=atoi(py),mm=atoi(pm),dd=atoi(pd); | |
if (yy>=1900&&yy<=9999) | |
{ | |
year = yy; | |
} | |
else | |
{ | |
return; | |
} | |
if (mm>=1&&mm<=12) | |
{ | |
month = mm; | |
} | |
else | |
{ | |
year=1900; | |
return; | |
} | |
if (dd>=1&&dd<=31) | |
{ | |
day = dd; | |
} | |
else | |
{ | |
year=1900; | |
month=1; | |
return; | |
} | |
} | |
// 测试 | |
int main() { | |
Date date1; // 使用无参构造函数 | |
cout << "date1:"; | |
date1.print_ymd(); | |
Date date2(2006); // 使用哪个构造函数? | |
cout << "date2:"; | |
date2.print_ymd(); | |
Date date3(2006,4); // 使用哪个构造函数? | |
cout << "date3:"; | |
date3.print_ymd(); | |
Date date4(2006,4,8); // 使用哪个构造函数? | |
cout << "date4:"; | |
date4.print_ymd(); | |
Date date5(2006,14,8); // 使用哪个构造函数? | |
cout << "date5:"; | |
date5.print_ymd(); | |
Date date6(date4); // 使用哪个构造函数? | |
cout << "date6:"; | |
date6.print_ymd(); | |
Date date7("2008-08-08"); // 使用哪个构造函数? | |
cout << "date7:"; | |
date7.print_ymd(); | |
return 0; | |
} |
# 析构函数
- 构造函数
Constructor
:在对象创建时执行,提供了初始化对象的一种简便手段。 - 析构函数
Destructor
:在对象被撤销时 (前) 执行,用于完成对象被撤销前的一些清理工作。
具体地说,析构函数往往用于释放 “资源”,如在构造函数中动态申请的内存空间。
也可以被用来执行 “用户希望在最后一次使用对象之后所执行的任何操作”,例如输出有关信息等。
# 对象的存储空间
若一个类声明了五个对象,存储空间如何分配?
每个对象中的数据成员分配了存储空间。
所有对象中的函数成员共用一个存储空间,在代码区存放。
# 定义方法
<类名>::~<类名>() | |
{ | |
<函数体> | |
} |
注意:
- 函数名是类名前面加
~
符号 - 析构函数不得返回任何值
- 析构函数不得带有任何参数
- 其主要功能是在撤销对象之前进行一些善后处理工作
- 由系统自动调用
例子:日期类中增加析构函数
#include<iostream> | |
using namespace std; | |
class Date | |
{ | |
int year,month,day; | |
public: | |
// 无参构造函数 | |
Date():year(1900),month(1),day(1) { } | |
// 默认参数构造函数 | |
Date(int yy,int mm = 1,int dd = 1); | |
// 日期对象参数构造函数 | |
Date(Date &d):year(d.year),month(d.month),day(d.day){}; | |
// 字符串日期构造函数 | |
Date(char *ps); | |
void print_ymd(); | |
~Date() { }; // 析构函数定义 | |
// 由于析构函数是空函数 (函数体中没有一条语句),没有感觉到析构函数被自动运行 | |
}; |
# 实际意义的析构函数
机器人的特征
姓名
型号
整数:待翻译的整数
翻译的英文句子字符串:字符指针,句子有长有短
……
机器人的功能
翻译整数函数:形成英文字符串,并将字符串指针返回
构造函数
设置函数
输出英文句子函数
析构函数
class robot | |
{ | |
private: | |
// 机器人姓名 | |
char name[20]; | |
// 机器人型号 | |
char type[20]; | |
public: | |
// 构造函数 | |
robot() | |
{ | |
strcpy(name,"XXXXXX"); | |
strcpy(type,"XXXXXX"); | |
} | |
// 设置修改数据 | |
void set(charn[],chart[]) | |
{ | |
strcpy(name,n); | |
strcpy(type,t); | |
} | |
// 英文中每三位数读法相同,所以定义 out 函数翻译小于 1000 的整数 | |
void out(inta); | |
// 将 1 至 1999999999 的整数翻译成英文句子 | |
void tran_int(intn); | |
// 析构函数 | |
~robot(){}; | |
}; | |
// 定义两个全局字符指针数组,存取所需的单词 | |
//num1 中为 1 到 19,空出了 0,所以可以直接用 num1 [n] 调用,得到 n 对应单词 | |
static char *num1[]= | |
{ | |
"","one ","two ","three ","four ","five ","six ","seven ","eight ", | |
"nine ","ten ","eleven ","twelve ", "thirteen ","fourteen ", | |
"fifteen ","sixteen ","seventeen ","eighteen ","nineteen " | |
}; | |
//num10 中为 20-90,空出了 0 和 1,所以可以直接用 num10 [n/10] 调用,得到 n 对应单词 | |
static char *num10[]= | |
{ | |
"","","twenty ","thirty ","forty ","fifty ","sixty ","seventy ", | |
"eighty ","ninety " | |
}; | |
// 小于 1000 整数翻译函数 | |
void robot::out(int a) | |
{ | |
int b=a%100; | |
// 若百位不为零,输出百位数加 hundred, | |
// 若此时十位个位均为 0,不加 and | |
if(a/100!=0) | |
{ | |
cout<<num1[a/100]<<"hundred "; | |
if(b!=0) | |
{ | |
cout<<"and "; | |
} | |
} | |
// 当后两位在 20 以内时,直接调用 num1 [n],输出 | |
if(b<20) { | |
cout<<num1[b]; | |
} | |
// 当后两位大于 20 时 | |
else | |
{ | |
// 先调用 num10,输出十位数 | |
cout<<num10[b/10]; | |
// 个位不为 0 时应输出 "-" 个位数 | |
if(b%10!=0) | |
{ | |
cout<<"\b-"<<num1[b%10]; | |
} | |
} | |
} | |
// 整数翻译函数 | |
void robot::tran_int(int n) | |
{ | |
if(n>1999999999) | |
{ | |
cout<<"dev C++平台无法处理大于1999999999位的数!"<<endl; | |
} | |
else | |
{ | |
// 三位三位取出,存入 abcd 中 | |
int a=n/1000000000, | |
b=(n%1000000000)/1000000, | |
c=(n%1000000)/1000, | |
d=n%1000; | |
/// 当 abcd 不等于 0 时,输出并加上 billion, million 或 thousand | |
if(a!=0) | |
{ | |
out(a); | |
cout<<"billion "; | |
} | |
if(b!=0) | |
{ | |
out(b); | |
cout<<"million "; | |
} | |
if(c!=0) | |
{ | |
out(c); | |
cout<<"thousand "; | |
} | |
if(d!=0) | |
{ | |
// 据英文语法规则,最后两位前一定有 and | |
if(d<100&&(a!=0||b!=0||c!=0)) { | |
cout<<"and "; | |
} | |
out(d); | |
} | |
cout<<endl; | |
} | |
} | |
int main() { | |
int n; | |
cout<<"请输入n:"; | |
cin>>n; | |
cout<<n<<endl; | |
robot brown; | |
brown.tran_int(n); | |
return 0; | |
} |
class robot | |
{ | |
private: | |
// 机器人姓名 | |
char name[20]; | |
// 机器人型号 | |
char type[20]; | |
// 待翻译的整数 | |
int num; | |
// 指向英文字符串 | |
char *ps; | |
public: | |
// 构造函数 | |
robot() | |
{ | |
strcpy(name,"XXXXXX"); | |
strcpy(type,"XXXXXX"); | |
num=0; | |
ps=new char[5]; | |
strcpy(ps,"zero"); | |
} | |
// 设置修改数据 | |
void set(charn[],chart[]) | |
{ | |
strcpy(name,n); | |
strcpy(type,t); | |
} | |
// 英文中每三位数读法相同,所以定义 out 函数翻译小于 1000 的整数 | |
char *out(int a); | |
// 将 1 至 1999999999 的整数翻译成英文句子 | |
char *tran_int(int n); | |
// 输出整数及其英文句子 | |
void print_num(); | |
// 析构函数 | |
~robot(){ | |
// 释放构造函数和 set 函数中动态申请的空间 | |
delete [] ps;// [] 表示 ps 指向的字符数组,整个数组都要被释放 | |
}; | |
}; | |
// 定义两个全局字符指针数组,存取所需的单词 | |
//num1 中为 1 到 19,空出了 0,所以可以直接用 num1 [n] 调用,得到 n 对应单词 | |
static char *num1[]= | |
{ | |
"","one ","two ","three ","four ","five ","six ","seven ","eight ", | |
"nine ","ten ","eleven ","twelve ", "thirteen ","fourteen ", | |
"fifteen ","sixteen ","seventeen ","eighteen ","nineteen " | |
}; | |
//num10 中为 20-90,空出了 0 和 1,所以可以直接用 num10 [n/10] 调用,得到 n 对应单词 | |
static char *num10[]= | |
{ | |
"","","twenty ","thirty ","forty ","fifty ","sixty ","seventy ", | |
"eighty ","ninety " | |
}; | |
// 类外定义小于 1000 整数翻译函数 | |
char *robot::out(int a) | |
{ | |
// 定义字符数组 k,长度设为 1000,用空字符串初始化,用来存储英文字符串 | |
char k[1000]=""; | |
int b=a%100; | |
// 若百位不为零,输出百位数加 hundred, | |
// 若此时十位个位均为 0,不加 and | |
if(a/100!=0) | |
{ | |
// 不直接输出字符串,而是加入字符数组 k 中 | |
strcat(k,num1[a/100]); | |
strcat(k,"hundred "); | |
if(b!=0) { | |
strcat(k,"and "); | |
} | |
} | |
// 当后两位在 20 以内时,直接调用 num1 [n],输出 | |
if(b<20) { | |
strcat(k,num1[b]); | |
} | |
// 当后两位大于 20 时 | |
else | |
{ | |
// 先调用 num10,输出十位数 | |
strcat(k,num10[b/10]); | |
// 个位不为 0 时应输出 "-" 个位数 | |
if(b%10!=0) | |
{ | |
strcat(k,"\b-"); | |
strcat(k,num1[b%10]); | |
} | |
} | |
// 动态申请一个存储空间 | |
char *p=new char[strlen(k)+1]; | |
// 大小则根据 k 中实际字符串的长度 + 1 | |
// 调用完毕需要释放该空间 | |
strcpy(p,k); | |
return p; | |
} | |
// 类外定义整数翻译函数 | |
char *robot::tran_int(int n) | |
{ | |
char *p; // 定义字符指针 p | |
char kk[1000]=""; // 定义字符数组 kk,用来存放被翻译出的英文字符串,初始化成空字符串 | |
if(n>1999999999) | |
{ | |
strcpy(kk,"dev C++平台无法处理太大的数!\n"); | |
} | |
else | |
{ | |
// 三位三位取出,存入 abcd 中 | |
int a=n/1000000000, | |
b=(n%1000000000)/1000000, | |
c=(n%1000000)/1000, | |
d=n%1000; | |
/// 当 abcd 不等于 0 时,输出并加上 billion, million 或 thousand | |
if(a!=0) | |
{ | |
p=out(a); | |
strcpy(kk,p); | |
strcat(kk,"billion "); | |
delete [] p; // 释放在 out 函数中动态申请的空间 | |
} | |
if(b!=0) | |
{ | |
p=out(b); | |
strcat(kk,p); | |
strcat(kk,"million "); | |
delete [] p; // 释放在 out 函数中动态申请的空间 | |
} | |
if(c!=0) | |
{ | |
p=out(c); | |
strcat(kk,p); | |
strcat(kk,"thousand "); | |
delete [] p; // 释放在 out 函数中动态申请的空间 | |
} | |
if(d!=0) | |
{ | |
// 据英文语法规则,最后两位前一定有 and | |
if(d<100&&(a!=0||b!=0||c!=0)) { | |
strcat(kk,"and "); | |
} | |
p=out(d); | |
strcat(kk,p); | |
delete [] p; // 释放在 out 函数中动态申请的空间 | |
} | |
p=new char[strlen(kk)+1]; | |
strcpy(p,kk); | |
return p; | |
} | |
} | |
// 类外定义设置函数 | |
void robot::set(char n[],char t[],int m) // 设置修改数据 | |
{ | |
strcpy(name,n); | |
strcpy(type,t); | |
if(num==m) { | |
// 待翻译的整数没有变 | |
return; | |
} | |
else | |
{ | |
num=m; | |
delete [] ps; // 删除已有的英文句子 | |
} | |
if(num>0) { | |
char *tp=tran_int(num); | |
ps=new char[strlen(tp)+1]; | |
strcpy(ps,tp); | |
delete [] tp; // 释放在 trans_int 中动态申请的空间 | |
} | |
else if(num==0) | |
{ | |
ps=new char[5]; | |
strcpy(ps,"zero"); | |
} | |
else | |
{ | |
ps=new char[13]; | |
strcpy(ps, "负数不能翻译"); | |
} | |
} | |
int main() { | |
robot brown; | |
brown.print_num(); | |
int n; | |
cout<<"请输入n:"; | |
cin>>n; | |
brown.set("brown","800#",n); | |
brown.print_num(); | |
return 0; | |
} |
- 存储空间的实际分配
当运行int n;
与robot brown;
后,
存储空间分配如下:
Error: Failed to launch the browser process! spawn /Applications/Google Chrome.app/Contents/MacOS/Google Chrome ENOENT TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md
new
运算符操作结果
当运行brown.set(“brown”, “800#”,n);
后,
堆区有字符串空间被占用,具体空间分配如下:
Error: Failed to launch the browser process! spawn /Applications/Google Chrome.app/Contents/MacOS/Google Chrome ENOENT TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md
空析构函数的实际作用
当析构函数为空函数时:~robot() { }
运行return
语句后,空间1
释放,而空间2
保留析构函数的实际作用
当析构函数为:~robot() { delete [] ps; }
运行return
语句后,空间1
空间2
均释放
# 对象与指针
class Person | |
{ | |
private: | |
char Name[9]; | |
char Sex; | |
int Age; | |
char Pid[19]; | |
char *Addr; | |
public: | |
Person() | |
{ | |
strcpy(Name,"xxxxxxxx"); | |
Age = 0; | |
Sex = 'x'; | |
strcpy(Pid,"xxxxxxxxxxxxxxxxxx"); | |
Addr=NULL; | |
} | |
Person(char *N, int A, char S,char *P,char *Ad) | |
{ | |
strcpy(Name,N); | |
Age = A; | |
Sex = S; | |
strcpy(Pid,P); | |
int L=strlen(Ad); | |
Addr=new char[L+1]; | |
strcpy(Addr,Ad); | |
} | |
~Person() | |
{ | |
cout<<"Now destroying Person"<<Name<<endl; | |
delete [] Addr; | |
} | |
void Register( char *name, int age, char sex,char *pid,char *addr); | |
void ShowMe(); | |
}; | |
void Person::Register(char *name,int age,char sex,char *pid,char *addr) | |
{ | |
strcpy(Name,name); | |
Age = age; | |
Sex = sex; | |
strcpy(Pid,pid); | |
if(Addr!=NULL) { | |
delete [] Addr; | |
} | |
int L=strlen(addr); | |
Addr=new char[L+1]; | |
strcpy(Addr,addr); | |
} | |
void Person::ShowMe() | |
{ | |
cout<<Name<<" "<<Age<<" "<<Sex<<" "<<Pid<<" "; | |
if(Addr!=NULL) { | |
cout<<Addr; | |
} | |
cout<<endl; | |
} | |
int main() | |
{ | |
Person person1; | |
Person person2("张三",19,'m',"610103199409192839","中国西安市咸宁西路29号"); | |
Person person3; | |
cout<<"person1: "; | |
person1.ShowMe(); | |
cout<<"person2: "; | |
person2.ShowMe(); | |
person3.Register("李四",29,'w',"610103198409192493","中国上海市华山北路1845号"); | |
cout<<"person3: "; | |
person3.ShowMe(); | |
person2.Register("赵五",29,'w',"610103198409152127","中国新疆乌鲁木齐市王家沟12号"); | |
cout<<"person2: "; | |
person2.ShowMe(); | |
return 0; | |
} |
# 指向对象的指针
<类名> *<指针变量名表>; |
Person person1("Zhang3",19,'f'); | |
Person *ptr=&person1; | |
ptr->ShowMe(); |
# 动态存储
<对象指针>=new <类名>(<名字初始化值>); | |
delete <名字指针>; |
Person *p=new Person; | |
delete p; |
#include<iostream> | |
using namespace std; | |
class Date | |
{ | |
public: | |
int year,month,day; | |
void init(int y,int m,int k); | |
void print_ymd(); | |
}; | |
void Date::init(int yy,int mm,int dd) | |
{ | |
month = mm; | |
year = yy; | |
day = dd; | |
} | |
void Date::print_ymd() | |
{ | |
cout<<year<<"-"<<month<<"-"<<day<<endl; | |
} | |
int main() | |
{ | |
Date date1; | |
Date *p1 = &date1; | |
p1->init(2006,5,13); | |
p1->print_ymd(); | |
int *p2 = &date1.year; | |
cout << *p2 <<endl; | |
void (Date::*p3)(int ,int ,int); // 普通函数指针定义不行 | |
void (Date::*p4)(); | |
p3 = Date::init; | |
p4 = Date::print_ymd; | |
(date1.*p3)(2006,4,8); | |
(date1.*p4)(); | |
return 0; | |
} |
#include<iostream> | |
#include<string.h> | |
using namespace std; | |
class Person | |
{ | |
private: | |
char Name[9]; | |
char Sex; | |
int Age; | |
public: | |
Person() | |
{ | |
strcpy(Name,"XXX"); | |
Age = 0; | |
Sex = ' '; | |
} | |
~Person() | |
{ | |
cout<<"Now destroying Person"<<endl; | |
} | |
void Register( char *name, int age, char sex); | |
void ShowMe(); | |
}; | |
void Person::Register(char *name,int age,char sex) | |
{ | |
strcpy(Name,name); | |
Age = age; | |
Sex = sex; | |
} | |
void Person::ShowMe() | |
{ | |
cout<<Name<<'\t'<<Age<<'\t'<<Sex<<endl; | |
} | |
int main() | |
{ | |
Person *p1,*p2; // 声明两个指向对象的指针 | |
p1=new Person; // 动态生成一个 Person 对象 | |
cout << "person1: \t"; | |
p1->ShowMe(); | |
p1->Register("Zhang3", 19, 'm'); | |
cout << "person1: \t"; | |
p1->ShowMe(); | |
p2=new Person; // 动态生成一个 Person 对象 | |
cout << "person2: \t"; | |
p2->ShowMe(); | |
*p2 = *p1; // 对象之间的赋值 | |
cout << "person2: \t"; | |
p2->ShowMe(); | |
delete p1; // 释放 p1 指针指向对象所占的空间 | |
delete p2; // 释放 p2 指针指向对象所占的空间 | |
return 0; | |
} |
# this
指针
每一个类的成员函数都包含一个指向本类对象的指针,指针名为
this
。该指针指向本类对象的起始地址
this
是指针,*this
就是对象(本对象)访问成员
this->成员
(*this).成员
.
运算符优先级比较高,故需要使用()
#include <iostream> | |
using namespace std; | |
class Test | |
{ | |
int x; | |
public: | |
Test( int = 0 ); | |
void print(); | |
}; | |
// 构造函数 | |
Test::Test(int a) | |
{ | |
x = a; | |
} | |
void Test::print() | |
{ | |
cout << " x = " << x <<endl; | |
cout << "this->x = " << this->x << endl; | |
cout << "(*this).x = " << ( *this ).x << endl; | |
} | |
int main() | |
{ | |
Test testObject( 12 ); | |
testObject.print(); | |
return 0; | |
}// 如果在 print () 函数中增加 int x=13; 输出结果如何变化? |
- 常用场合
- 当类中数据成员名与成员函数中的形参名相同时,用
this
指针加以区分
class Time | |
{ | |
private: | |
int hour,minute,second; | |
public: | |
void set(int hour,int minute,int second) | |
{ | |
this->hour = hour; | |
this->minute = minute; | |
this->second = second; | |
} | |
} |
// 两个整数为参数的构造函数 | |
Fraction::Fraction(int a,int b) | |
{ | |
set(a,b); // 调用成员函数 | |
} | |
// 设置分子、分母 | |
void Fraction::set(int a,int b) | |
{ | |
this->a=a; | |
this->b=b; | |
} | |
// 分数相加,本类对象加 u | |
Fraction Fraction::add(Fraction u) | |
{ | |
int tmp; | |
Fraction v; | |
v.a=a*u.b+b*u.a; // 分子 | |
v.b=b*u.b;// 分母 | |
tmp=divisor(v.a ,v.b);// 计算分子、分母的公约数 | |
v.a=v.a/tmp;// 约去公约数 | |
v.b=v.b/tmp;// 约去公约数 | |
return v; // 返回结果 | |
} |
- 成员函数需要返回指针或本对象时
// 分数相加,本类对象加 u | |
Fraction Fraction::add(Fraction u) | |
{ | |
int tmp; | |
// Fraction v; | |
a=a*u.b+b*u.a; // 分子 | |
b=b*u.b;// 分母 | |
tmp=divisor(a ,b);// 计算分子、分母的公约数 | |
a=a/tmp;// 约去公约数 | |
b=b/tmp;// 约去公约数 | |
return *this; // 返回结果 | |
} |
# 课堂讨论
- 如何通过指针访问对象数组的元素的成员。
如如何通过PERSON Beijing[100];
PERSON *p=Beijing;
p
访问第i
个对象的成员,如show()
,set(char *name,char *gender, int age)
等公有成员。
p
是指针,p+i
也是指针。指针指向->
*p
是对象,*(p+i)
也是对象,对象用访问.
(*(p+i)).show() ; | |
(p+i)->show(); |
- 指向类中成员函数的指针如何定义?它与一般函数指针定义的区别是什么?
一般函数指针的调用与类中的静态成员函数并无区别。因为类的静态成员不属于任何对象。 但与类的非静态成员函数指针有区别。因为类的非静态成员函数中有一个隐形的 this 指针,使得其与一般函数指针不兼容。并且类与类之间的 this 指针也是不一样的,也就是指向不同类的非静态成员函数指针间也是不兼容的。因而需要指出非静态成员函数指针所属的类,即在指针前需要加上参数名,表示指针是指向某一类的非静态成员函数。
- this 指针与对象指针的区别是什么?this 指针的用途?
this 指针:在每一个成员函数中都包含一个特殊的指针,这个 this 指针的名字是固定的,称为 this。它是指向本类对象的指针,它的值是当前被调用的成员函数所在的对象的起始地址。
一个对象的 this 指针并不是对象本身的一部分,不会影响对象的结果。this 作用域是在类内部,编译器自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上 this 指针,编译器在编译的时候也是加上 this 的,它作为非静态成员函数的隐含形参,对各成员的访问均通过 this 进行。对象指针:指存放对象初始地址的指针变量。它包括
- 指向对象的指针(如
int *p1;//定义指向整型数据的指针变量
)- 类成员指针,又分为数据成员指针(
类名::*指针变量名=成员地址初值
,如Date::*p=d;
)和成员函数指针(函数返回类型 类名::*指针变量名 函数参数
,如void (*pf)(char a, const char *b);
)
- this 指针与对象指针的区别:this 指针在一般的成员函数内会有,且名字固定,它的值是当前调用该成员函数的对象的地址;对象指针是指向对象的。
- this 指针的用途:当参数有跟该对象里的成员变量重名的时候,可以用 this 指针来解决变量重名带来的问题。
# 随堂练习
下面关于构造函数的叙述,正确的是 。
构造函数名必须与类名相同。
构造函数由系统自动调用运行。
下面关于析构函数的叙述,正确的是 。
析构函数名与类名相同。
p
是指向PERSON
类对象的指针,则通过p
访问公有成员函数show()
的方法是。this 指针需要事先声明,才能使用。