第五次课主要是学习的类的继承和派生,联系前面几次课学的类基础,构造函数,拷贝构造函数,析构函数,运算符重载等知识。
程序填空,输出指定结果
#include
#include
using namespace std;
int strlen(const char * s)
{ int i = 0;
for(; s[i]; ++i);
return i;
}
void strcpy(char * d,const char * s)
{
int i = 0;
for( i = 0; s[i]; ++i)
d[i] = s[i];
d[i] = 0;
}
int strcmp(const char * s1,const char * s2)
{
for(int i = 0; s1[i] && s2[i] ; ++i) {
if( s1[i] < s2[i] )
return -1;
else if( s1[i] > s2[i])
return 1;
}
return 0;
}
void strcat(char * d,const char * s)
{
int len = strlen(d);
strcpy(d+len,s);
}
class MyString
{
//在这里补充你的代码;
};
int CompareString( const void * e1, const void * e2)
{
MyString * s1 = (MyString * ) e1;
MyString * s2 = (MyString * ) e2;
if( * s1 < *s2 )
return -1;
else if( *s1 == *s2)
return 0;
else if( *s1 > *s2 )
return 1;
}
int main()
{
MyString s1("abcd-"),s2,s3("efgh-"),s4(s1);
MyString SArray[4] = {"big","me","about","take"};
cout << "1. " << s1 << s2 << s3<< s4<< endl;
s4 = s3;
s3 = s1 + s3;
cout << "2. " << s1 << endl;
cout << "3. " << s2 << endl;
cout << "4. " << s3 << endl;
cout << "5. " << s4 << endl;
cout << "6. " << s1[2] << endl;
s2 = s1;
s1 = "ijkl-";
s1[2] = 'A' ;
cout << "7. " << s2 << endl;
cout << "8. " << s1 << endl;
s1 += "mnop";
cout << "9. " << s1 << endl;
s4 = "qrst-" + s2;
cout << "10. " << s4 << endl;
s1 = s2 + s4 + " uvw " + "xyz";
cout << "11. " << s1 << endl;
qsort(SArray,4,sizeof(MyString),CompareString);
for( int i = 0;i < 4;i ++ )
cout << SArray[i] << endl;
//s1的从下标0开始长度为4的子串
cout << s1(0,4) << endl;
//s1的从下标5开始长度为10的子串
cout << s1(5,10) << endl;
return 0;
}
样例输入
无
样例输出
原型:char *strcpy(char *dst,const char *src)
功能:将以src为首地址的字符串复制到以dst为首地址的字符串,包括’\0’结束符,返回dst地址。要求:src和dst所指内存区域不可以重叠且dst必须有足够的空间来容纳src的字符串,若dst空间不足,编译时并不会报错
int strlen(const char * s)
{ int i = 0;
for(; s[i]; ++i);
return i;
}
void strcpy(char * d,const char * s)
{
int i = 0;
for( i = 0; s[i]; ++i)
d[i] = s[i];
d[i] = 0;
}
int strcmp(const char * s1,const char * s2)
{
for(int i = 0; s1[i] && s2[i] ; ++i) {
if( s1[i] < s2[i] )
return -1;
else if( s1[i] > s2[i])
return 1;
}
return 0;
}
void strcat(char * d,const char * s)
{
int len = strlen(d);
strcpy(d+len,s);
}
//尤其是重写的strcat()方法,赶紧记笔记。
void strcat(char * d,const char * s)
{
int len = strlen(d);
strcpy(d+len,s);
}
MyString s1(“abcd-”),s2,s3(“efgh-”),s4(s1); 这告诉我们需要补充无参构造函数,有参构造函数和拷贝构造函数。
cout << "1. " << s1 << s2 << s3<< s4<< endl;需要重载流提取运算符。
s3 = s1 + s3;重载加法运算符
cout << "6. " << s1[2] << endl;重载[]运算符
s1 += “mnop”;重载+=运算符。
s4 = “qrst-” + s2;重载加法运算符,注意这和s3 = s1 + s3;重载加法运算符不一样,所以加法运算符的有两个重载函数
cout << s1(0,4) << endl;重载(int,int)运算符
if( * s1 < *s2 ) if( *s1 == *s2) if( *s1 > *s2 ) 重载小于 大于 等于比较运算5
4‘‘,9hhy7y888876 6 ’’符
//三种构造函数
MyString(){ //无参构造函数
str = "";
}
MyString(const char * s){ //传入字符串为参数的构造函数
if(s){
str = new char[strlen(s) + 1];
strcpy(str, s);
}
else
str = NULL;
}
MyString(const MyString & s){ //拷贝构造函数
str = new char[strlen(s.str) + 1];
strcpy(str, s.str);
}
MyString operator()(int i, int j){ //重载()
MyString res; //i为子串开始位置,j为子串长度
res.str = new char[j];
int start = i;
int index = 0;
for(index = 0; index < j; index++){
res.str[index] = str[start++];
}
res.str[index] = '\0';
return res;
}
char & operator[](int i){return str[i];} //重载[]
friend ostream& operator<<(ostream &os,const MyString &s){ //重载流提取运算符
if(s.str == NULL)
return os;
else
{
os << s.str;
return os;
}
}
friend MyString operator+(const char * str, const MyString &rhs){ //这里重载加法运算
MyString res(str); //为了应对 "abcd" + s 的形式
res = res + rhs; //为什么采用友元,因为既可以应对 "abcd" + s
return res; //也可以应对s + "abcd";
}
MyString operator+(const MyString & rhs){ //重载加法运算符
MyString res;
int n = strlen(str) + strlen(rhs.str)+1; //为了应对 s1 + s2
res.str = new char[n];
strcpy(res.str, str);
strcat(res.str, rhs.str);
res.str[n-1] = '\0';
return res;
}
MyString operator+=(const char * rhs){ //重载+=运算符
MyString res;
MyString r(rhs);
res = *this + r; //利用*this 简化编程
*this = res;
return *this;
}
//重载小于 大于 等于比较运算符
bool operator < (const MyString & rhs){
int flag = strcmp(str, rhs.str);
if(flag == -1)
return true;
else
return false;
}
bool operator > (const MyString & rhs){
int flag = strcmp(str, rhs.str);
if(flag == 1)
return true;
else
return false;
}
bool operator == (const MyString & rhs){
int flag = strcmp(str, rhs.str);
if(flag == 0)
return true;
else
return false;
}
int CompareString( const void * e1, const void * e2)
{
MyString * s1 = (MyString * ) e1;
MyString * s2 = (MyString * ) e2;
if( * s1 < *s2 )
return -1;
else if( *s1 == *s2)
return 0;
else if( *s1 > *s2 )
return 1;
}
friend MyString operator+(const char * str, const MyString &rhs){ //这里重载加法运算
MyString res(str); //为了应对 "abcd" + s 的形式
res = res + rhs; //为什么采用友元,因为既可以应对 "abcd" + s
return res; //也可以应对s + "abcd";
}
这里应对 “abcd” + s 的形式的技巧值得学习,先将 “abcd” 放在一个新的对象里面进行对象的加分
public:
MyString(){
str = NULL;//这里写NULL比""好,写NULL没有警告
String str=null
这句话的意思就是定义一个字符串,变量str,字符串的内容为空值。
String str=“”
定义一个String类型的变量str,并为其赋值。
String str=null与String str=“”区别
1.""分配了内存;null没有分配内存。
2.""是一个字符串(String).它在内存中是存在的.而null它是一个空对象.在内存中是不存在的。
3.""占内存,在内存中会分配一个空间。
null不占内存. 为空引用.
String str1= null; str引用为空
String str2= “”; str应用一个空字符串
也就是null没有分配空间,""分配了空间,因此str1还不是一个实例化的对象,而str2已经实例化。
注意null不是对象,""是对象。
总结:
null表示的是一个对象的值,而并不是一个字符串。例如声明一个对象的引用,String a = null ;
“”表示的是一个空字符串,也就是说它的长度为0。例如声明一个字符串String str = “” ;
内存分配
String str= null ; 表示声明一个字符串对象的引用,但指向为null,也就是说还没有指向任何的内存空间;
String str= “”; 表示声明一个字符串类型的引用,其值为”“空字符串,这个str引用指向的是空字符串的内存空间;
#include
#include
using namespace std;
int strlen(const char * s)
{ int i = 0;
for(; s[i]; ++i);
return i;
}
void strcpy(char * d,const char * s)
{
int i = 0;
for( i = 0; s[i]; ++i)
d[i] = s[i];
d[i] = 0;
}
int strcmp(const char * s1,const char * s2)
{
for(int i = 0; s1[i] && s2[i] ; ++i) {
if( s1[i] < s2[i] )
return -1;
else if( s1[i] > s2[i])
return 1;
}
return 0;
}
void strcat(char * d,const char * s)
{
int len = strlen(d);
strcpy(d+len,s);
}
class MyString
{
private:
char * str;
public:
MyString(){
str = NULL;//这里写NULL比""好,写NULL没有警告
}
MyString(const char * s){
if(s){
str = new char[strlen(s) + 1];
strcpy(str, s);
}
else
str = NULL;
}
MyString(const MyString & s){
str = new char[strlen(s.str) + 1];
strcpy(str, s.str);
}
MyString operator()(int i, int j){
MyString res;
res.str = new char[j];
int start = i;
int index = 0;
for(index = 0; index < j; index++){
res.str[index] = str[start++];
}
res.str[index] = '\0';
return res;
}
char & operator[](int i){return str[i];}
friend ostream& operator<<(ostream &os,const MyString &s){
if(s.str == NULL)
return os;
else
{
os << s.str;
return os;
}
}
friend MyString operator+(const char * str, const MyString &rhs){
MyString res(str);
res = res + rhs;
return res;
}
MyString operator+(const MyString & rhs){
MyString res;
int n = strlen(str) + strlen(rhs.str)+1;
res.str = new char[n];
strcpy(res.str, str);
strcat(res.str, rhs.str);
res.str[n-1] = '\0';
return res;
}
MyString operator+=(const char * rhs){
MyString res;
MyString r(rhs);
res = *this + r;
*this = res;
return *this;
}
bool operator < (const MyString & rhs){
int flag = strcmp(str, rhs.str);
if(flag == -1)
return true;
else
return false;
}
bool operator > (const MyString & rhs){
int flag = strcmp(str, rhs.str);
if(flag == 1)
return true;
else
return false;
}
bool operator == (const MyString & rhs){
int flag = strcmp(str, rhs.str);
if(flag == 0)
return true;
else
return false;
}
};
int CompareString( const void * e1, const void * e2)
{
MyString * s1 = (MyString * ) e1;
MyString * s2 = (MyString * ) e2;
if( * s1 < *s2 )
return -1;
else if( *s1 == *s2)
return 0;
else if( *s1 > *s2 )
return 1;
}
int main()
{
MyString s1("abcd-"),s2,s3("efgh-"),s4(s1);
MyString SArray[4] = {"big","me","about","take"};
cout << "1. " << s1 << s2 << s3<< s4<< endl;
s4 = s3;
s3 = s1 + s3;
cout << "2. " << s1 << endl;
cout << "3. " << s2 << endl;
cout << "4. " << s3 << endl;
cout << "5. " << s4 << endl;
cout << "6. " << s1[2] << endl;
s2 = s1;
s1 = "ijkl-";
s1[2] = 'A' ;
cout << "7. " << s2 << endl;
cout << "8. " << s1 << endl;
s1 += "mnop";
cout << "9. " << s1 << endl;
s4 = "qrst-" + s2;
cout << "10. " << s4 << endl;
s1 = s2 + s4 + " uvw " + "xyz";
cout << "11. " << s1 << endl;
qsort(SArray,4,sizeof(MyString),CompareString);
for( int i = 0;i < 4;i ++ )
cout << SArray[i] << endl;
//s1的从下标0开始长度为4的子串
cout << s1(0,4) << endl;
//s1的从下标5开始长度为10的子串
cout << s1(5,10) << endl;
return 0;
}
程序填空,输出指定结果
#include
#include
#include
#include
using namespace std;
class MyString:public string
{
};
int main()
{
MyString s1("abcd-"),s2,s3("efgh-"),s4(s1);
MyString SArray[4] = {"big","me","about","take"};
cout << "1. " << s1 << s2 << s3<< s4<< endl;
s4 = s3;
s3 = s1 + s3;
cout << "2. " << s1 << endl;
cout << "3. " << s2 << endl;
cout << "4. " << s3 << endl;
cout << "5. " << s4 << endl;
cout << "6. " << s1[2] << endl;
s2 = s1;
s1 = "ijkl-";
s1[2] = 'A' ;
cout << "7. " << s2 << endl;
cout << "8. " << s1 << endl;
s1 += "mnop";
cout << "9. " << s1 << endl;
s4 = "qrst-" + s2;
cout << "10. " << s4 << endl;
s1 = s2 + s4 + " uvw " + "xyz";
cout << "11. " << s1 << endl;
sort(SArray,SArray+4);
for( int i = 0;i < 4;i ++ )
cout << SArray[i] << endl;
//s1的从下标0开始长度为4的子串
cout << s1(0,4) << endl;
//s1的从下标5开始长度为10的子串
cout << s1(5,10) << endl;
return 0;
}
样例输入
无
样例输出
提示 1:如果将程序中所有 “MyString” 用 “string” 替换,那么除了最后两条红色的语句编译无法通过外,其他语句都没有问题,而且输出和前面给的结果吻合。也就是说,MyString 类对 string 类的功能扩充只体现在最后两条语句上面。
提示 2: string 类有一个成员函数 string substr(int start,int length); 能够求从 start 位置开始,长度为 length 的子串
提示 3: C++中,派生类的对象可以赋值给基类对象,因为,一个派生类对象,也可看作是一个基类对象(大学生是学生)。反过来则不行(学生未必是大学生) 同样,调用需要基类对象作参数的函数时,以派生类对象作为实参,也是没有问题的
这道题和第一题的输入输出完全一样,关键是对String类的领悟。
提示里面有讲MyString 类对 string 类的功能扩充只体现在最后两条语句上面。那说明除了取子串的操作之外都是string类实现了的操作。
public:
MyString():string(){}
MyString(const MyString &A):string(A){}
MyString(const char * S):string(S){}
MyString(const string & A):string(A){}
//类型转换构造函数, 把string对象转换成MyString 使得string 的+ 可以用 和最后的()可以用
MyString operator()(int n, int m){
return substr(n,m);
}
提示 4:这里用派生类的对象给基类对象赋值,就相当于给派生类本身赋值,只是这里可以调用string类的性质。
问:MyString(const MyString &A):string(A){}这里使用string的构造函数初始化,都是最后是怎么实现MyString s1(“abcd-”),s4(s1);怎么返回到s4对象来的过程没能理解????
答:这里s4里面的派生类成员变量是继承的string类里面的,所以调用指针进行复制等操作的时候,其实就相当于在派生类里面操作,因为一个派生类的内存里面也包含了基类。
在公有派生的情况下,可以说,派生类对象也是基类对象,任何本该出现基类对象的地方,如果出现的是派生类对象,也是没问题的。这里可以将string看做就是Mystring,然后用string可以调用基类的构造函数,就不用派生类自己写
string类构造函数源代码
String::String(const String &str)//
{
if(NULL == str.p_str)
{
return;
}
strLength = str.strLength;
p_str = new char[strLength+1];
strcpy(p_str,str.p_str);
}
#include
#include
#include
#include
using namespace std;
class MyString:public string
{
public:
MyString():string(){}
MyString(const MyString &A):string(A){}//用string()初始化构造函数
MyString(const char * S):string(S){}
MyString(const string & A):string(A){}
//类型转换构造函数, 把string对象转换成MyString 使得string 的+ 可以用 和最后的()可以用
MyString operator()(int n, int m){
return substr(n,m);
}
};
int main()
{
MyString s1("abcd-"),s2,s3("efgh-"),s4(s1);
MyString SArray[4] = {"big","me","about","take"};
cout << "1. " << s1 << s2 << s3<< s4<< endl;
s4 = s3;
s3 = s1 + s3;
cout << "2. " << s1 << endl;
cout << "3. " << s2 << endl;
cout << "4. " << s3 << endl;
cout << "5. " << s4 << endl;
cout << "6. " << s1[2] << endl;
s2 = s1;
s1 = "ijkl-";
s1[2] = 'A' ;
cout << "7. " << s2 << endl;
cout << "8. " << s1 << endl;
s1 += "mnop";
cout << "9. " << s1 << endl;
s4 = "qrst-" + s2;
cout << "10. " << s4 << endl;
s1 = s2 + s4 + " uvw " + "xyz";
cout << "11. " << s1 << endl;
sort(SArray,SArray+4);
for( int i = 0;i < 4;i ++ )
cout << SArray[i] << endl;
//s1的从下标0开始长度为4的子串
cout << s1(0,4) << endl;
//s1的从下标5开始长度为10的子串
cout << s1(5,10) << endl;
return 0;
}
代码填空,使得程序能够自动统计当前各种动物的数量
#include
using namespace std;
void print() {
cout << Animal::number << " animals in the zoo, " << Dog::number << " of them are dogs, " << Cat::number << " of them are cats" << endl;
}
int main() {
print();
Dog d1, d2;
Cat c1;
print();
Dog* d3 = new Dog();
Animal* c2 = new Cat;
Cat* c3 = new Cat;
print();
delete c3;
delete c2;
delete d3;
print();
}
样例输入
None
样例输出
0 animals in the zoo, 0 of them are dogs, 0 of them are cats
3 animals in the zoo, 2 of them are dogs, 1 of them are cats
6 animals in the zoo, 3 of them are dogs, 3 of them are cats
3 animals in the zoo, 2 of them are dogs, 1 of them are cats
这个题和第三周的题目完全一样,考查的是类的继承,创建一个基类Animal类,派生类Dog和Cat类继承自Animal类,使用一个静态成员变量来统计数量。
class Animal
{
public:
static int number; //统计动物数量
Animal()
{
number++;
}
virtual ~ Animal() //基类析构函数添加virtual关键字
{
number--;
}
};
class Dog:public Animal
{
public:
static int number;
Dog()
{
number++;
}
~Dog()
{
number--;
}
};
class Cat:public Animal
{
public:
static int number;
Cat()
{
number++;
}
~Cat()
{
number--;
}
};
int Animal::number = 0; //静态成员变量需要初始化
int Cat::number = 0;
int Dog::number = 0;
这里要注意的点就是:
程序中出现了
Animal* c2 = new Cat;
delete c2;
通过基类的指针删除派生类对象时,通常情况下只调用基类的析构函数
但是,删除一个派生类的对象时,应该先调用派生类的析构函数,然后调用基类的析构函数。
解决办法:把基类的析构函数声明为virtual
派生类的析构函数可以virtual不进行声明
通过基类的指针删除派生类对象时,首先调用派生类的析构函数,然后调用基类的析构函数
一般来说,一个类如果定义了虚函数,则应该将析构函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析构函数定义成虚函数。
注意:不允许以虚函数作为构造函数