(一二八)比较成员函数、中括号表示法、静态成员函数
来源:互联网 发布:1.76我本沉默复古淘宝 编辑:程序博客网 时间:2024/04/27 23:06
有比较函数是strcmp (参数1, 参数2)
参数是两个字符串,所在头文件是<string>
比较方法是按顺序依次比较参数1和参数2的第一个字符(看ASCII值)。
假如相同,则比较下一个;
假如参数1的比参数2的大,则返回1(正数);
假如参数1的比参数2的小,则返回-1(负数);
假如两个字符串完全一样,则返回0。
其原理是(这个我自己写的):
int strcmp(const char* a, const char* b){while (*a == *b){if (*a == *b == 0)return 0;a++, b++;}if (a > b) return 1;else if (a < b)return -1;}
注意:
①具体实现可能有所区别,但都是逐个判断;
②strcmp函数里的指针移动,不影响函数外的指针(因为这里是按值传递)。
可以通过比较函数,编写类的比较成员函数,或者是友元函数。
作用是,比较一个字符串和一个类对象(事实上是这个类对象的某个同类成员)的关系。
例如,代码:
bool operator<(char* word){if (strcmp(name, word) < 0)return true;//这里不能使用string类。注:string类的比较可以直接用>或者<或者==else return false;}bool operator>(char*word){return word < name;//word<name和name>word是一个意思。假如前者为真,则后者为真,返回true}bool operator==(char*word){return (strcmp(name, word) == 0);//如果等于0,则说明相等,返回true,否则false}
注意:
①strcmp的两个参数,是char*类型,因此不能使用string类型进行比较;
②string类型的比较,可以直接使用 >、<、= 比较2个string类字符串的大小(返回true或者false,效果同这种方法);
③为了方便,有必要加入友元函数进行比较,方法类似,如:
bool operator<(const char*word, Player& m)
{
return (m>word);
}
用中括号表示法访问字符:
中括号表示法其实就是“[]”这个符号。
在之前,假如有一个指针指向一个字符串char*a="abcd"; 那么a[0]=a;
char *a = new char[5];
strcpy_s(a, 5,"abcd");
a[0] = 'b';
cout << a << endl;
而这样一段代码中,a[0]可以把第一个字符改为字符'b',
同样,可以用运算符重载的形式,将这种方法用作类(注意,中括号只能作为成员函数重载)。
如代码:
#include<iostream>#include<string>using namespace std;class Man{string fname;string lname;public:Man() { fname = "aaa";lname = "bbb"; }//为了方便,使用默认构造函数char&operator[](int m);void show();};int main(){Man a;cout << a[0] << endl;//a[0]是对象a的私有成员lname的第一个字母a[0] = 'c';a.show();system("pause");return 0;}void Man::show(){cout << fname << ", " << lname << endl;}char&Man::operator[](int m){return lname[m];}
显示:
baaa, cbb请按任意键继续. . .
注意:
①a[0]中,a表示的是调用[]重载运算符的对象,0是参数。(可以参照指针指向字符串来理解)。
②因为返回的是引用char&,因此,这种方法可以修改私有成员的数据(a[0] = 'c';);
③假如涉及到对象数组,例如Man b[3],那么b[0]表示的则不是对象的第一个字符,而是表示的是b[0]这个对象。如果需要表示第一个字符,则使用b[0][0]。
④具体返回什么,可以根据需要自行定义。这里返回的是私有成员lname的某个字符(根据参数决定)。
假如这里面对是被const关键字所限定的对象(例如返回的、或者调用的对象是const所限定的const Man a;),那么则需要修改函数定义,具体需要看情况。
①假如是成员被const所限定,那么,首先肯定不能直接返回引用(因为引用涉及修改),可以返回被const所限定的引用、或者是返回副本(即比如说返回char类型)。
如:const string lname="bbb";
则函数定义修改为:
const char& Man::operator[](int m)
{
return lname[m];
}
这里的const表示返回值被const所限定,因此不能成为左值。
②假如是类对象被const所限定,如:const Man a;,这里表示对象的成员不能被修改。那么:
(1)需要有符合其的重载定义(成员函数在括号后加const表示限定成员不能被修改);
(2)而返回值不能直接返回引用(因为这样可能导致会被修改),应返回普通类型或者是被const所限定的引用。
如:
const char& Man::operator[](int m)const
{
return lname[m];
}
第一个const表示返回值被限定,第二个const表示成员对象被限定。
③因此,如果要为被const限定的对象和不被const限定的对象(后者方便修改,且前提是返回值的成员没有被const限定)我们可以同时准备两个中括号运算符重载的定义,当遇见被const限定的对象时,则匹配const版的,否则匹配非const版的。
char& Man::operator[](int m)
{
return lname[m];
}
const char& Man::operator[](int m)const
{
return lname[m];
}
④另外,如果无需修改的话,可以直接使用const版的(放弃无const版),未被const限定的对象,也可以匹配const版的函数使用。
静态成员函数:
静态成员函数有点类似类的静态数据成员。
首先,静态成员函数的函数声明,必须包含关键字static;(但如果函数定义是独立的——指不属于任何一个类,而静态成员函数必须属于一个类,则不能使用关键字static)
其次,静态成员函数,在类外定义的话,无需再次加入static表示其是静态函数,只需要加上作用域运算符即可。例如类Man的静态成员函数static int show();在定义的时候,函数原型是int Man::show()这样
第三,不能通过 对象名.函数名 这样调用静态成员函数(因为他不属于某一个对象,属于整个类),也因此,无法使用this指针(因为this指针指向的是当前对象)。静态成员函数的调用方法是: 类名::静态成员函数名 ;
最后,静态成员函数,只能访问同类里的静态成员(因为是他不属于对象,所以不能访问属于对象的非静态成员)。
这也就是为什么静态私有成员可以在函数外声明,而不能在类的非成员函数和非友元函数中访问。是因为静态成员属于整个类(而类定义不分配内存)。
而静态成员函数,可以用于设置类级(classwide)标记,以控制某些接口的行为。例如,类级标记可以控制显示类内容的方法所用的格式。
比如说在某种情况下,调用一次静态成员函数(可以用 类型::静态成员函数名 这样的方式),然后把静态数据成员+1,于是,某些成员函数以静态数据成员为判断条件的,则可以改变执行的代码)。
另外,静态私有数据成员之所以要在类定义和函数定义之外声明,两个原因:
①非成员、友元函数内部不能访问类的私有成员;
②成员函数、友元函数在能使用的时候,已经创建了类对象了,而多个类对象共享一个静态数据成员,因此需要先有静态数据成员,后有类对象才可以;(但若全局声明一个类对象,再初始化静态数据成员似乎也没有影响,不过这种对象在静态内存区域,好吧,我也搞不懂)
③不能和类在同一个头文件声明。是因为头文件可以多次重复引用,会导致多重声明。
而#ifndef #endif 和#pragma once的作用,是防止在同一个文件内多次引用头文件,但对于多个文件引用同一个头文件,并没有作用。
赋值运算符的再次重载:
假如有构造函数:Man(const char* a);
那么若使用Man a = "abc";
编译器则会调用构造函数,创建一个临时对象("abc"作为参数),然后使用复制构造函数,将临时对象的赋值给对象a,再然后调用析构函数,删除这个临时对象。
如代码:
#include<iostream>#include<string>using namespace std;class Man{string fname;string lname;static int a;public:Man() { fname = "aaa";lname = ""; }//为了方便,使用默认构造函数Man(const char* m);void show();~Man() { std::cout << "1" << std::endl; }};int main(){Man a;a = "aaa";system("pause");return 0;}void Man::show(){cout << fname << ", " << lname << endl;}Man::Man(const char* m){lname = m;}
显示:
1请按任意键继续. . .
这里是1,就表示析构函数被调用了,否则无显示。
当类对象比较小的时候(成员很少),可能没什么大的影响。但若类成员比较多(比如有100个),那么就会影响程序效率(因为会调用构造函数生成临时对象,还要再调用析构函数删除这个对象)。
因此,可以考虑重载赋值运算符,参数为const char*
新的运算符重载函数定义为:
int Man::operator=(const char*m)
{
lname = m;
return 1;
}
此时,重新运行程序,这次便无任何显示了。注意,假如是char*指针,则不能直接用lname=m这种形式,而是应通过delete 和new来进行。
另外,之所以需要有返回值,是因为赋值运算符的表达式本身就有值,例如:
cout<< (a=0) <<endl; 便会显示0,而 cout<< (a=100) <<endl; 则会显示100。而cout << (a = 'b') << endl;则显示的为字符b。
也就是说,其输入什么,在原本的赋值运算符中,其返回值便是什么。
而这里我们自定义的赋值运算符重载,返回值可以由我们自己决定。
以上便是类定义的优化,现我根据书上更改后的类声明,自行编写类定义,然后用书上给的程序进行测试。
#ifndef STRING1_H_
#define STRING1_H_
#include<iostream>
using std::ostream;
using std::istream;
class String
{
private:
char * std;
int len;
static int num_strings;
static const int CINLIM = 80;
public:
String(const char * s);
String();
String(const String &);
~String();
int length()const {return len;}
String & operator=(const String &);
String & operator=(const char*);
char & operator[](int i);
const char & operator[] (int i) const;
friend bool operator<(const String &st, const String& st2);
friend bool operator>(const String &st1, const String &st2);
friend bool operator==(const String &st, const String &st2);
friend ostream & operator<<(ostream & os, const String &st);
friend istream & operator>>(istream & is, String &st);
statici nt HowMany();
};
#endif
以上是类定义,
现将完成程序如下:
//1.h 头文件,类定义#ifndef STRING1_H_#define STRING1_H_#include<iostream>using std::ostream;using std::istream;class String{private:char * std;int len;static int num_strings;static const int CINLIM = 80;//输入限制public:String(const char * s);String();String(const String &);~String();int length()const { return len; }//返回字符串长度String & operator=(const String &);String & operator=(const char*);char & operator[](int i);const char & operator[] (int i) const;friend bool operator<(const String &st, const String& st2);friend bool operator>(const String &st1, const String &st2);friend bool operator==(const String &st, const String &st2);friend ostream & operator<<(ostream & os, const String &st);friend istream & operator>>(istream & is, String &st);static int HowMany();};#endif//2.cpp 类的成员函数定义#include<iostream>#include"1.h"String::String(const char * s)//构造函数,用于将字符串作为参数{len = strlen(s);std = new char[len + 1];strcpy_s(std, len + 1, s);num_strings++;//计数器+1}String::String()//默认构造函数{len = 6;std = new char[len + 1];strcpy_s(std, len + 1, "未命名");num_strings++;}String::String(const String & m)//复制构造函数,新建一个对象,并将其初始化为同类现有对象时调用,需要增加计数器{len = m.len;std = new char[len + 1];strcpy_s(std, len + 1, m.std);num_strings++;}String::~String()//析构函数,需要delete对象new出来的动态内存{delete[]std;num_strings--;}String & String::operator=(const String & m)//赋值运算符重载,无需增加计数器{delete[]std;//因为是已存在对象,然后下面要new,所以这里需要先deletelen = m.len;std = new char[len + 1];strcpy_s(std, len + 1, m.std);return *this;}String & String::operator=(const char*m)//赋值运算符重载,用于将字符串赋给类对象时调用,不增加计数器,因为这个用来避免创造临时对象{if (*this == m)return *this;//防止将自己赋值给自己delete[]std;//已存在对象,下面要new,因此deletelen = strlen(m);std = new char[len + 1];strcpy_s(std, len + 1, m);return *this;}char & String::operator[](int i)//中括号运算符重载,用于返回第i个字符{return std[i];}const char & String::operator[] (int i) const//中括号运算符重载,用于返回第i个字符,面向被const限定对象{return std[i];}bool operator<(const String &st, const String& st2)//用于比较类对象大小(实质上是比较类对象的字符串的数值大小——逐个比较){return (strcmp(st.std, st2.std) < 0);}bool operator>(const String &st1, const String &st2){return st2 < st1;}bool operator==(const String &st, const String &st2){return (strcmp(st.std,st2.std) == 0);}ostream & operator<<(ostream & os, const String &st)//输出运算符重载{os << st.std << "的长度是:" << st.len;return os;}istream & operator>>(istream & is, String &st)//输入运算符重载{delete[]st.std;char *q = new char[100];//用一个间接的储存输入的内容is.get(q, String::CINLIM);while (is.get() != '\n')is.get();//由于要求清除换行符,所以不能直接用is>>q这种形式,需要读取掉换行符。st.len = strlen(q);st.std = new char[st.len + 1];strcpy_s(st.std, st.len + 1, q);//然后复制进去delete[]q;//删除这个间接的return is;}int String::HowMany()//返回当前对象数量,注意,这里的定义不再加static表示是静态成员函数{return num_strings;}//1.cpp,用于测试#include<iostream>#include"1.h"const int ArSize = 10;const int MaxLen = 81;int String::num_strings = 0;//静态变量int main(){using std::cout;using std::cin;using std::endl;String name;cout << "Hi,what's your name?\n>>";cin >> name;cout << name << ", please enter up to " << ArSize << " short sayings <empty line to quit>:\n";String sayings[ArSize];char temp[MaxLen];int i;for (i = 0;i<ArSize;i++){cout << i + 1 << ": ";cin.get(temp, MaxLen);while (cin && cin.get() != '\n')continue;if (!cin || temp[0] == '\0')break;elsesayings[i] = temp;}int total = i;if (total>0){cout << "Here are your sayings:\n";for (i = 0;i < total; i++)cout << sayings[i][0] << ": " << sayings[i] << endl;int shortest = 0;int first = 0;for (int i = 1;i<total;i++){if (sayings[i].length()<sayings[shortest].length())shortest = i;if (sayings[i]<sayings[first])first = i;}cout << "Shortest saying:\n" << sayings[shortest] << endl;cout << "First alphabetically:\n" << sayings[first] << endl;cout << "This program used " << String::HowMany() << " String objects. Bye.\n";}elsecout << "No input! Bye.\n";system("pause");return 0;}
测试结果:
Hi,what's your name?>>wdwd的长度是:2, please enter up to 10 short sayings <empty line to quit>:1: asda2: befbe3: qwwqfwqf4: fdvfdvav5: avavdasv6: szccvdv7: ththeth8: q9: fdgafg10: rgergHere are your sayings:a: asda的长度是:4b: befbe的长度是:5q: qwwqfwqf的长度是:8f: fdvfdvav的长度是:8a: avavdasv的长度是:8s: szccvdv的长度是:7t: ththeth的长度是:7q: q的长度是:1f: fdgafg的长度是:6r: rgerg的长度是:5Shortest saying:q的长度是:1First alphabetically:asda的长度是:4This program used 11 String objects. Bye.请按任意键继续. . .
总结:
①犯错的一个地方在于:istream & operator>>(istream & is, String &st)//输入运算符重载 这个函数,只记得delete,但忘记new。因此退出程序时会出错,补充后ok。
②在①函数中,第一次未添加while (is.get() != '\n')is.get();出错,表现是未读取换行符。添加用于读取换行符后正常。
③没注意到原代码是在类成员函数定义文件中声明的静态变量,导致出错一次。在1.cpp文件中补充后正常。
④假如输入空行提前结束输入,会导致依然显示11 String objects,原因在于测试程序的循环是这样的,会要求建立10个新对象,再加上String name这个对象,因此共计11个。
⑤另一个错误在于,在赋值运算符重载时(面向类对象的),忘记加入面对自身时的条件判断了。这个错误会导致假如把自己赋值给自己,可能会出错的问题。
- (一二八)比较成员函数、中括号表示法、静态成员函数
- 仿函数 括号重载 谓词函数比较类数据成员
- 静态成员变量成员函数
- 在静态函数中访问成员函数
- 静态成员(变量、函数)
- C++中静态成员函数访问非静态成员变量
- C++中静态成员函数访问非静态成员变量
- C++中静态数据成员和静态成员函数
- static 静态成员变量 静态成员函数 类中使用
- C++中静态成员函数访问非静态成员变量
- c++中静态成员函数访问非静态成员变量
- C++中静态成员函数访问非静态成员变量
- C++中静态成员函数访问非静态成员变量
- C++中静态成员变量和静态成员函数
- c++中静态成员函数和静态成员变量
- C++中静态数据成员以及静态成员函数
- C++中静态成员函数访问非静态成员变量
- C++中静态成员变量与静态成员函数
- leetcode之 Longest Common Prefix
- 在iTunes Connect查看崩溃日志
- Session过期
- kernel开机logo总结
- POJ 1035(字符串模拟)
- (一二八)比较成员函数、中括号表示法、静态成员函数
- QT 通过POST方法,上传文件到服务器
- msdn.itellyou.cn
- [Android]自定义圆形图片
- android app 快速接入银联支付流程(android studio版)
- python中函数参数,函数调用参数传递,可变参数,*expression,**expression
- hadoop 2.6.0 安全问题--UserGroupInformation
- 父子页面的相互调用
- kafka基本概念