c++自定义String
mystring.c
#include "mystring.h"
#define SIZE 8
#include"mystring.h"
//计算所需最小满足len的容量 每次都是8的倍数
int max_size(int len,int maxsize){
while(len>maxsize){
maxsize*=2;
}
return maxsize;
}
//构造函数
mystring::mystring(){
//初始化
size=0;
maxsize=SIZE;
str=new char[size];
cout<<"无参构造"<<endl;
}
//有参构造
mystring::mystring(const char * str1){
//初始化
size=strlen(str1);
maxsize=max_size(size+1,SIZE);//计算出最小满足len的容量 需要考虑'\0'
str=new char[maxsize];
//拷贝
memcpy(str,str1,size+1);//需要考虑'\0'
cout<<"mystring::有参"<<endl;
}
mystring::mystring(int n,char ch){
maxsize=max_size(n+1,SIZE);//\0
size=n;
str=new char[maxsize];
int i=0;
for(i=0;i<size;i++){
str[i]=ch;
}
str[i]='\0';
}
//析构函数
mystring::~mystring(){
delete []str;
cout<<"析构"<<endl;
}
//拷贝构造函数
mystring ::mystring(const mystring &other){
//初始化
if(other.str==NULL){return ;}
size=other.size;
maxsize=other.maxsize;
str=new char[maxsize];
//拷贝
memcpy(str,other.str,maxsize);
cout<<"拷贝构造函数"<<endl;
}
//拷贝赋值函数
mystring & mystring::operator=(const mystring &other){
size=other.size;
maxsize=other.maxsize;
delete []str;
str=new char[maxsize];
memcpy(str,other.str,maxsize);
cout<<"拷贝赋值函数"<<endl;
return *this;
}
mystring & mystring::operator=(const char * str1){
//初始化
size=strlen(str1);
maxsize=SIZE;
maxsize=max_size(size+1,maxsize);
str=new char[maxsize];//需要考虑'\0'
//拷贝
memcpy(str,str1,size+1);
cout<<"拷贝赋值函数"<<endl;
return *this;
}
//判空函数
bool mystring::isempty(){
if(size==0){
return true;
}
return false;
}
//size函数
int mystring::mysize(){
if(str==NULL) return -1;
return size;
}
//c_str函数
char * mystring::c_str(){
return str;
}
//at函数
char & mystring::at(int n){
return str[n];
}
//二倍扩容
void mystring::expent(){
char * temp=new char[maxsize*2];
if(temp==NULL){
perror("expent");
return ;
}
memcpy(temp,str,maxsize);
delete []str;
str=temp;
maxsize*=2;
}
//实现+=运算符重载 s1 += s2
mystring & mystring::operator+=(const mystring &other){
//扩容
while(1){
//如果可以容纳就跳出循环
if(maxsize>=other.size+size+1)//注意\0
{
break;
}
//扩容
expent();
}
//追加
strcat(str,other.str);
return *this;
}
mystring & mystring::operator+=(const char* &str1){
//计算大小
int len=strlen(str1);
//扩容
while(1){
//如果可以容纳就跳出循环
if(maxsize>=len+size+1)//注意\0
{
break;
}
//扩容
expent();
}
//追加
strcat(str,str1);
return *this;
}
//取地址运算符重载
mystring * mystring::operator&(){
cout<<"取地址运算重载"<<endl;
return this;
}
char & mystring::operator [](int n){
return str[n];
}
mystring mystring::operator +(mystring other){
while(1){
//如果可以容纳就跳出循环
if(maxsize>=other.size+size+1)//注意\0
{
break;
}
//扩容
expent();
}
//追加
strcat(str,other.str);
return *this;
}
//关系函数
bool mystring::operator ==(mystring &n){
return strcmp(str,n.c_str())==0;
}
bool mystring::operator !=(mystring &n){
return strcmp(str,n.c_str())!=0;
}
bool mystring::operator <=(mystring &n){
return strcmp(str,n.c_str())<0||strcmp(str,n.c_str())==0;;
}
bool mystring::operator >=(mystring &n){
return strcmp(str,n.c_str())>0||strcmp(str,n.c_str())==0;;
}
bool mystring::operator >(mystring &n){
return strcmp(str,n.c_str())>0;
}
bool mystring::operator <(mystring &n){
return strcmp(str,n.c_str())<0;
}
//输入输出
ostream & operator <<(ostream &cout,mystring & other){
cout<<other.c_str();
return cout;
}
istream & operator >>(istream &cin,mystring & other){
//定义缓冲区
char buf[1024];
cin>>buf;
//获取长度
other.size=strlen(buf);
//比较是否大于最大长度,大于就修改最大长度并且申请内存
if(other.size+1>other.maxsize){
//计算最小所需内存空间
other.maxsize=max_size(other.size+1,other.maxsize);
char * temp=new char[other.maxsize];
delete [] other.str;
other.str=temp;
}
//拷贝
strcpy(other.str,buf);
return cin;
}
mystring.h
#ifndef MYSTRING_H
#define MYSTRING_H
#include<iostream>
#include<cstring>
using namespace std;
class mystring
{
char *str;
int size;
public:
int maxsize;//记录容量
mystring();
mystring(const char *);
mystring(int n,char ch);
~mystring();
//*******//
//拷贝构造函数
mystring(const mystring &other);
//拷贝赋值函数
mystring & operator=(const mystring &other);
mystring & operator=(const char * str1);
//判空函数
bool isempty();
//size函数
int mysize();
//c_str函数
char * c_str();
//at函数
char & at(int n);
//二倍扩容
void expent();
//实现+=运算符重载 s1 += s2
mystring & operator+=(const mystring &other);
mystring & operator+=(const char* &);
//取地址运算符重载
mystring * operator&();
//访问指定字符
char & operator [](int n);
mystring operator +(mystring );
//关系函数
bool operator ==(mystring &n);
bool operator !=(mystring &n);
bool operator <=(mystring &n);
bool operator >=(mystring &n);
bool operator >(mystring &n);
bool operator <(mystring &n);
//输入输出
friend ostream & operator <<(ostream &cout,mystring & other);
friend istream & operator >>(istream &cin,mystring & other);
};
//输入输出
ostream & operator <<(ostream &cout,mystring & other);
istream & operator >>(istream &cin,mystring & other);
#endif // MYSTRING_H
学习笔记
运算符重载
算数类运算符重载
- 1> 种类:+ 、-、*、/、%、&、| 。。。。。
- 2> 使用格式: L # R //L表示左操作数,#表示运算符号 R表示右操作数
- 3> 左操作数L : 可以是左值也可以是右值,运算后不会被更改
- 4> 右操作数:可以是左值也可以是右值,运算后不会被更改
- 5> 运算结果:是一个右值,临时空间,结果不能被改变
- 6> 函数定义格式:
- 全局函数版:const 类名 operator#(const 类名 &L, const 类名 &R )
- 成员函数版:const 类名 operator#(const 类名 &other) const
关系运算符重载
- 1> 种类:> 、<、==、!=、>=、<=
- 2> 使用格式: L # R //L表示左操作数,#表示运算符号 R表示右操作数
- 3> 左操作数L : 可以是左值也可以是右值,运算后不会被更改
- 4> 右操作数:可以是左值也可以是右值,运算后不会被更改
- 5> 运算结果:是一个bool类型的右值
- 6> 函数定义格式:
- 全局函数版:bool operator#(const 类名 &L, const 类名 &R )
- 成员函数版:bool operator#(const 类名 &other) const
单目运算符
单目运算符重载
- 1> 种类:!(逻辑非)、~(按位取反)、 - (负号)、&(取地址运算符)、*(取值运算符)
- 2> 使用格式:#O //#表示运算符号 O表示操作数
- 3> 操作数O : 可以是左值也可以是右值,运算后不会被更改
- 4> 运算结果:同类的右值 (*取值运算除外)
- 6> 函数定义格式:
- 全局函数版:类名 operator#(const 类名 &O )
- 成员函数版:类名 operator#() const
自增自减运算符
后置自增
- 使用格式 :O++
- 操作数0 只能是左值
- 运算结果是自增的右值
- 函数定义格式
- 全局函数版:类名 operator # (const 类名 &o,int)
- 成员函数: 类名 operator #(int ) const
前置自增
- 使用格式:++O //O表示操作数
- 操作数O : 只能是左值
- 运算结果:自身的引用
- 函数定义格式:
- 全局函数版:类名 operator#(const 类名 &O )
- 成员函数版:类名 operator#() const
插入和提取运算符重载(<< 和 >>)
介绍
cin和cout对象的来源:cin是istream的一个类对象 cout是ostream的一个类对象
- 2> 当使用<<或者>>进行运算时:cout << 类对象
本质上:cout.operator<<(类对象) - 3> 如果要重载插入和提取运算符,可以使用成员函数版和全局函数版
但是,如果想要完成成员函数版,需要在istream或者ostream类中进行修改类体,难度很大,也没有必要- 此时,就只能使用全局函数版。使用全局函数版,那么就需要在运算符两次的操作数所在的类对象内将全局函数进行声明成友元
- 由于友元的作用是为了访问操作数的私有成员,在运算过程中,不需要访问cin或cout的私有成员,所以,无需在istream或者ostream类中,将全局函数设置成友元,只需在自定义类中将全局函数设置成友元即可
- 4> 使用格式:cin>>对象名 或者 cout<<对象名
- 5> 左操作数:istream或者ostream类对象
- 6> 右操作数:自定义的类对象
- 7> 结果:左操作数自身引用
- 格式
ostream & operator << (ostream & cout,类名 & R)
stream & operator >> (istream & cin,类名 &R)
静态成员函数
静态成员变量
- 1> 定义格式:在函数头前加关键字 static ,那么该函数就是静态成员函数
- 2> 静态成员函数,不依赖于某个类对象而独立存在,说明无需实例化对象,就可以调用该函数
- 3> 静态成员函数调用方式也有两种:
- 通过类对象进行调用
- 通过类名直接调用
- 4> 静态成员函数相当于将全局函数封装在类体内,通过类名直接使用
- 5> 静态成员变量,没有 this 指针,在静态成员函数中,只能使用静态成员变量,不能使用非静态成员变量
- 6> 类中静态成员函数与同名的非静态成员函数不构成重载关系
匿名对象
- 1> 匿名对象就是没有名字的对象,直接调用类的构造函数完成定义
- 2> 匿名对象的生命周期很短,仅仅只是在所在语句行内
- 3> 匿名对象的使用场景:
- 使用匿名对象给一个新对象进行初始化
- 使用匿名对象当做函数的形参
- 使用匿名对象给对象数组进行初始化
- 4> 匿名对象也可以像有名对象一样调用相关成员
一、继承(inherit)
面向对象的三大特征:封装、继承、多态
所谓继承:就是基于已有的类,去定义一个新类的过程就叫做继承
1.1 继承的引入
- 1> 程序员在定义某些类的时候,很多个类,可能有很多相似的地方,我们可以将这些相似点提取出来,定义为基类,其他所有子类继承自该类,即使在子类中,没有定义某些成员,该子类中也已经存在。
- 2> 继承的好处:
- 1、能够大大提高代码的复用性
- 2、继承是多态的必要条件,没有继承就没有多态
- 3> 一个类A继承自类B: 我们称 类B为父类、基类
类A为子类、派生类
1.2 继承格式
- 1> 格式
class 子类名 : 继承方式1 父类名1, 继承方式2 父类名2,。。。,继承方式n 父类名n
{
子类自己的成员
};
- 2> 一个子类可以由一个或多个父类共同派生出来,子类会继承所有父类中所有的成员
- 3> 继承方式:类的继承方式有三种,分别是public、protected、private
- 4> 默认的继承方式为private,但是常用的继承方式是public
父类的 public 成员 | 父类的 protected 成员 | 父类的 private 成员 | |
---|---|---|---|
public继承 | 子类中为 public | 子类中为 protected | 子类中无法访问 |
protected继承 | 子类中为 protected | 子类中为 protected | 子类中无法访问 |
private继承 | 子类中为 private | 子类中为 private | 子类中无法访问 |
在上述表格中,清晰地展示了在不同继承方式下,父类中不同访问修饰符的成员在子类中的访问属性变化情况 。在C++中,private
成员对于子类来说是完全不可见的,不能通过任何方式在子类中直接访问。而public
和protected
成员在不同继承方式下,在子类中的访问权限会发生相应的改变。
1.3 子类对父类继承步骤
1> 全盘吸收父类的所有成员,包括私有成员
2> 改造父类继承下来的成员,通过关键字 using
3> 拓展子类自己的成员