目录
一、编译命令
1、实例
g++ helloworld.cpp -o helloworld
二、引用
1、定义
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
2、创建与使用
int i = 17; int& r = i;//声明引用变量
r = 5;//使用引用变量
3、实例
#include <stdio.h>
int main()
{
int a = 100;
int &b = a;
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("addr: a = %p\n", &a);
printf("addr: b = %p\n", &b);
}
实验现象:
4、指针和引用的区别
1、指针有自己的一块空间,而引用只是一个别名;
2、使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小;
3、指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象的引用;
4、作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;
5、可以有const指针,但是没有const引用;
6、指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能被改变;
7、指针可以有多级指针(**p),而引用只有一级;
8、指针和引用使用++运算符的意义不一样;
9、如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。
三、函数重载
1、定义
函数可以使用相同的名称,但参数必须不同。
2、实例
intcmp(intdata1, intdata2 );
intcmp(constchar *str1,constchar *str2);
四、默认参数
1、定义
函数参数可以设定默认值。
2、创建与使用
void debug(constchar *ptr=“----------”);
3、实例
void debug(const char *ptr = "---------------")
{
printf("%s\n", ptr);
}
int main()
{
debug();
debug("hello");
debug("world");
}
五、堆内存
1、定义
C | C++ |
malloc() | new |
free() | delete |
2、创建与使用
Fun* ptr1 = new Fun;
delete ptr1;
3、delete和delete[]的区别
对于简单的数据类型而言,delete和delete[]都只是释放内存,没有什么区别。
如下:int *p = new int[4];delete p;与delete p [ ];结果是一样的。
其他情况:(待补充)
六、类与对象
1、定义
类是用来对实体(对象)进行描述的。(对象有什么属性,有什么功能)类是一种自定义类型;
方法是实现类功能的一个具体实现,该类有什么样的功能?类的所有功能都要通过调用方法来实现。
对象是一个实体,我们眼睛看到的所有实体都可以看成一个实体对象;
2、创建与使用
class 类名//或者struct 类名
{
//类体:成员变量--属性
//成员函数---功能
};
七、友元
1、定义
申明为 友元 可以调用类中的成员。
2、创建与使用
class A;
class B
{
public:
void printfA(A &x);
};
class A
{
public:
A()
{
x = 100;
}
// friend class B;
friend void B::printfA(A &x);//在A类中,申明B是A的友元
private:
int x;
};
void B::printfA(A &x)
{
printf("%d\n", x.x);
}
int main()
{
B b;
b.printfA(a);//B可以调用A中的友元函数
}
示例:实现顺序表
1、设计
1.建立顺序表
2.顺序表的初始化和销毁
3.输出顺序表
4.获取顺序表的长度
5.获取顺序表中指定位置的元素值
6.按照元素值查找位置
7.插入数据
8.删除数据
2、实现(目前只支持可显的一位字符)
SqList.h
#ifndef _SQLIST_H
#define _SQLIST_H
#define MAXSIZE 6 //顺序表最大长度
class SqList
{
private:
char *data;//存放顺序表元素
int length;//存放顺序表长度
public:
SqList(/* args */);//初始化顺序表
~SqList();//销毁顺序表
bool CreatsqList(int n);//创建顺序表
void DispList();//输出顺序表
int ListLength();//获取顺序表长度
bool isEmpty();//判断顺序表是否为空
bool getElemByIndex(int index, char &value);//查找某个位置的元素
int getIndexByElem(char val);//查找某个元素的位置
bool InsertSquList(int index, char value);//在某个位置插入元素
bool DeletElem(int index);//删除某个位置的元素
};
#endif
SqList.cpp
#include <iostream>
#include "SqList.h"
using namespace std;//调用命名空间std内定义的所有标识符
/**
* @brief 顺序表初始化
*
*/
SqList::SqList()
{
data = new char[MAXSIZE];//为顺序表分配长度为MAXSIZE的空间
length = 0;//初始化表长为0
}
/**
* @brief 顺序表销毁
*
*/
SqList::~SqList()
{
delete []data;//释放data分配的空间
length = 0;
}
/**
* @brief 创建顺序表
*
* @param n
* @return bool
*/
bool SqList::CreatsqList(int n)
{
if(n > MAXSIZE){
cout << "顺序表空间不足!";
length = 0;
return false;
}
for(int i = 0; i < n; i++){
cout << "请出入第" << i+1 << "个元素:";
cin >> data[i];
}
length = n;
return true;
}
/**
* @brief 输出顺序表
*
* @return void
*/
void SqList::DispList()
{
if(length > 0){
cout << "顺序表长度为"<< length << ",内容为:";
for (int i = 0; i < length; i++)
{
cout << data[i] << " ";
}
cout << endl;//输出结束
}else{
cout << "表为空" << endl;
}
}
/**
* @brief 获取顺序表长度
*
* @return int
*/
int SqList::ListLength()
{
return length;
}
/**
* @brief 判断顺序表是否为空
*
* @return bool
*/
bool SqList::isEmpty()
{
if(length > 0){
return false;
}else
{
return true;//为空
}
}
/**
* @brief 查找某个位置的元素
*
* @param index
* @param value
* @return bool
*/
bool SqList::getElemByIndex(int index, char &value)
{
if(index < 1 || index > length){
cout << "位置错误" << endl;
return false;
}else
{
value = data[index-1];
return true;
}
}
/**
* @brief 获得某个元素的位置
*
* @param value
* @return SqList::int 位置,从1开始
*/
int SqList::getIndexByElem(char value)
{
int i;
for(i = 0; i < length; i++){
if(data[i] == value){
break;
}
}
if(i >= length){
return 0;//没找到
}else{
return i+1;//从1开始,返回的是逻辑顺序
}
}
/**
* @brief 在某个位置插入元素
*
* @param index
* @param value
* @return SqList::bool
*/
bool SqList::InsertSquList(int index,char value)
{
if(value < 0 || value > 127){
cout << "注意:插入的数据不可显示!" << endl;
}
if(length >= MAXSIZE){
cout << "没有空间插入" << endl;
return false;
}
if(index < 1 || index > length){
cout << "位置错误" << endl;
return false;
}
cout << "在第" <<index << "位置插入" << value << endl;
for(int j = length; j >= index; j--){
data[j] = data[j-1];//将插入的位置以后的元素都后移一位
}
data[index-1] = value;
length++;
return true;
}
/**
* @brief 删除某个位置的元素
*
* @param index
* @return bool
*/
bool SqList::DeletElem(int index)
{
if(length <= 0){
cout << "没有内容删除" << endl;
return false;
}
if(index < 1 || index > length){
cout << "位置错误" << endl;
return false;
}
cout << "在第" << index << "位置删除一个元素" << endl;
for(int j = index-1; j < length -1; j++){
data[j] = data[j+1];//将插入的位置以后的元素都前移一位
}
length--;
return true;
}
一些注意事项:
char类型的值不在可显范围内的时候,cout打印不出来;
main.app
#include "SqList.h"
int main()
{
SqList mysqlist;
mysqlist.CreatsqList(3);
mysqlist.DispList();
mysqlist.InsertSquList(1,'5');
mysqlist.DispList();
mysqlist.DeletElem(1);
mysqlist.DispList();
}
编译执行
lynnzhang@lynnzhang-ubuntu20:~/Desktop/code/lv14_c++/day1/homework$ g++ main.cpp SqList.cpp -o sqlist -Wall
lynnzhang@lynnzhang-ubuntu20:~/Desktop/code/lv14_c++/day1/homework$ ./sqlist
实验现象
请出入第1个元素:2
请出入第2个元素:3
请出入第3个元素:4
顺序表长度为3,内容为:2 3 4
在第1位置插入5
顺序表长度为4,内容为:5 2 3 4
在第1位置删除一个元素
顺序表长度为3,内容为:2 3 4
八、深浅拷贝
1、构造拷贝函数
2、赋值运算符重载
3、实例
/**
* @file class_copy.cpp
* @author your name (you@domain.com)
* @brief 两种深拷贝
* 浅拷贝:简单的赋值拷贝操作。单纯赋值指针变量值。
* 深拷贝:在堆区重新申请空间,进行拷贝操作。复制指针指向的区域的值。
*
* @version 0.1
* @date 2022-07-09
*
* @copyright Copyright (c) 2022
*
*/
#include <stdio.h>
#include <string.h>
class A{
public:
A()
{
printf("A()\n");
p = new char[10];
strcpy(p, "hello");
printf("p: %s\n", p);
printf("p: %s\n", this->p);
}
A(const A &x)//深拷贝1
{
printf("A(const A &x)\n");
p = new char[10];
strcpy(p, x.p);
}
~A()
{
printf("~A()\n");
delete [] p;
}
A & operator=(A &x)//深拷贝2:赋值运算符重载为深拷贝
{
printf("operator=\n");
p = new char[10];
strcpy(p, x.p);
return *this;
}
private:
char *p;
};
int main()
{
A x;
A y(x);//深拷贝1
A z;
z = x;//深拷贝2
}
九、运算符重载
练习运算符重载:
* = 赋值运算符
* a++ 后++
* ++a 前++
* [] 成员运算符
* << 输出运算符
/**
* @file mytimer.cpp
* @author your name (you@domain.com)
* @brief 自定义一个定时器
* 练习运算符重载:
* = 赋值运算符
* a++ 后++
* ++a 前++
* [] 成员运算符
* << 输出运算符
*
* @version 0.1
* @date 2022-07-09
*
* @copyright Copyright (c) 2022
*
*/
#include <unistd.h>
#include <stdio.h>
#include <iostream>
using namespace std;
class Timer
{
private:
int hour;
int min;
int sec;
public:
Timer();//构造析构函数名必须与类型一样
~Timer();
void addtimer(int sec = 1);
void show(void);
Timer operator + (Timer &x);//运算符重载
Timer operator + (int sec);//运算符重载+函数重载
Timer operator ++ (int);//t++
Timer operator ++ (void);//++t
bool operator == (Timer &x);
int & operator[](int i);//成员运算符
friend ostream & operator << (ostream &out, const Timer &t);//友元函数,因为函数体内需要使用这个类的私有成员
};
/**
* @brief Construct a new Timer:: Timer object
*
*/
Timer::Timer()
{
hour = 0;
min = 0;
sec = 0;
}
/**
* @brief Destroy the Timer:: Timer object
*
*/
Timer::~Timer()
{
}
/**
* @brief
*
* @param sec
*/
void Timer::addtimer(int sec)//定义时不能放默认参数
{
this->min += (this->sec+ sec)/60;
this->sec = (this->sec+ sec)%60;//this指针指向的是类内的变量
this->hour += this->min / 60;
this->min = this->min % 60;
this->hour %= 24;
}
/**
* @brief
*
*/
void Timer::show(void)
{
printf("%02d:%02d:%02d\n",hour,min,sec);//需\n否则不刷新
}
/**
* @brief a+b
*
* @param x
* @return Timer
*/
Timer Timer::operator + (Timer &x)
{
Timer tem;
tem.sec = this->sec + x.sec;//能区分出来变量,可以不用this指针
tem.min = this->min + x.min;
tem.hour = this->hour = x.hour;
return tem;
}
/**
* @brief a+10
*
* @param sec
* @return Timer
*/
Timer Timer::operator + (int sec)
{
Timer tem;
tem.sec = this->sec + sec;
return tem;
}
/**
* @brief a++
*
* @return Timer
*/
Timer Timer::operator ++ (int)
{
Timer tem = *this;//把之前this里的值赋值了一份
sec ++;
return tem;
}
/**
* @brief ++a
*
* @return Timer
*/
Timer Timer::operator ++ (void)
{
sec ++;
return *this;
}
bool Timer::operator == (Timer &x)
{
if(sec == x.sec && min == x.min && hour == x.hour){
return true;
}else
{
return false;
}
}
/**
* @brief 成员运算符
*
* @param i
* @return int& 返回值可以用于左值
*/
int & Timer::operator[](int i)
{
switch (i)
{
case 0:
return hour;
case 1:
return min;
case 2:
return sec;
default:
return hour;
}
}
/**
* @brief 输出运算符重载
*
* @param out
* @param t
* @return ostream& 为左值,为了可以链式操作:<< xxx << xxx << xxx
*/
ostream & operator << (ostream &out, const Timer &t)
{
cout << "hour:" <<t.hour << "min:" << t.min << "sec:" << t.sec <<endl;
return out;
}
int main()
{
Timer t;
t.addtimer(3);
Timer t1;
t1.addtimer(5);
Timer t2;
t2= t+t1;
t2.show();
t2 = t+10;
t2.show();
Timer t3,t4;
t4 = ++t3;
t4.show();
t3.show();
if(t3 == t4){
printf("t3 == t4\n");
}
printf("hour:%d min:%d sec:%d\n",t3[0],t3[1],t3[2]);
t3[0] = 30;
printf("hour:%d min:%d sec:%d\n",t3[0],t3[1],t3[2]);
cout << t3 << t2;
#if 0
while (1)
{
t.addtimer(1);
t.show();
sleep(1);
}
#endif
}
十、仿函数重载
/**
* @file converter.cpp
* @author your name (you@domain.com)
* @brief 汇率转换
* 练习仿函数
* @version 0.1
* @date 2022-07-09
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>
/**
* @brief 开放std命名空间,就可以直接使用cout了
* 否则使用std::cout << "hello"<<std::endl;这样的形式
*
*/
using namespace std;
/**
* @brief 转换器类
*
*/
class Converter{
public:
/**
* @brief 创建一个构造函数
*
* @param rate 汇率
*/
Converter(double rate)
{
this->rate = rate;
}
/**
* @brief 仿函数,通过“对象()”触发
*
* @param rmb
* @return double
*/
double operator()(double rmb)
{
return rmb*rate;
}
private:
double rate;
};
double RMBto(double rmb, double rate)
{
return rmb*rate;
}
int main()
{
Converter RMBtoUS(6.4);
cout << RMBtoUS(10) << endl;//仿函数:对象触发了一个函数
Converter RMBtoE(8.4);
cout << RMBtoE(100) << endl;
}
十一、输入输出
#include <stdio.h>
#include <iostream>//标准输入输出流头文件
using namespace std;
int main()
{
// printf("input: ");fflush(stdout);//保证能刷新出数据
cout<<"input: ";
char buf[100];
// gets(buf);
cin >> buf;
// printf("%s\n", buf);
cout << dec << buf << endl;
std::cout<<10<<std::endl;
cout<<10<<endl;//自动匹配
cout<< hex <<10<<endl;//按照16进制打印
}
十二、类的继承和重载
/**
* @file 05、arr.cpp
* @author your name (you@domain.com)
* @brief 类的组合与继承
* @version 0.1
* @date 2022-07-09
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>
using namespace std;
class ARR{
public:
ARR():tail(0){//将tail初始化为0
}
void addtail(int data);
void show(void);
//private:
int data[100];
int tail;
};
void ARR::addtail(int data)
{
this->data[tail++] = data;
}
void ARR::show(void)
{
int i = 0;
for(;i<tail; i++)
printf("%d, ", data[i]);
printf("\n");
}
/**
* @brief 继承类
* ARR为基类,ARRX为ARR的派生类
*
*/
class ARRX:public ARR{//public:继承的时候保持基类的属性
public:
/**
* @brief 求平均值
*
* @return int
*/
int ever(void)
{
int i = 0;
int sum = 0;
for(;i<tail; i++)
sum += data[i];
return sum/tail;
}
};
/**
* @brief 学生系统管理类,组合类
*
*/
class Stuma{
public:
Stuma(){
}
~Stuma() { }
/**
* @brief 保存成绩到数组中
*
* @param score
*/
void savescore(int score)
{
scorearr.addtail(score);
}
int everscore(void)
{
return scorearr.ever();
}
/**
* @brief 查看成绩
*
*/
void showscore(void)
{
scorearr.show();
}
private:
ARRX scorearr;//组合类
};
int main()
{
Stuma mmm;
mmm.savescore(23);
mmm.savescore(44);
mmm.savescore(55);
mmm.savescore(23);
mmm.showscore();
cout << mmm.everscore() <<endl;
}
1、继承权限
1、public继承方式
- 基类中所有public成员在派生类中为public属性;
- 基类中所有protected成员在派生类中为protected属性;
- 基类中所有private成员在派生类中不可访问。
2、protected继承方式
- 基类中的所有public成员在派生类中为protected属性;
- 基类中的所有protected成员在派生类中为protected属性;
- 基类中的所有private成员在派生类中仍然不可访问。
3、 private继承方式
- 基类中的所有public成员在派生类中均为private属性;
- 基类中的所有protected成员在派生类中均为private属性;
- 基类中的所有private成员在派生类中均不可访问。
2、proteced和private的区别
1、private是完全私有的,只有当前类中的成员能访问到。
2、protected是受保护的,只有当前类的成员与继承该类的类才能访问。
3、这两个是访问类中成员权限的限制符。在类外如果想使用类中的成员,只能直接使用public类型的,protected和private都是不能访问的,对于类外使用而言,这两个是完全相同的。
示例:模仿c++的string类,自己实现string类
1、设计
作业:模仿c++的string类,自己实现string类
String类的常用方法
1.String的构造方法
1)String(String original):把字符串数据封装成字符串对象
2)String(char[] value):把字符数组的数据封装成字符串对象
3)String(char[] value, int index, int count):把字符数组中的一部分数据封装成字符串对象
2.String类的获取功能:
1)length():获取字符串的长度,其实也就是字符个数
2)charAt(int index):获取指定索引处的字符
3)indexOf(String str):获取str在字符串对象中第一次出现的索引
4)substring(int start):从start开始截取字符串
5)String substring(int start,int end):从start开始,到end结束截取字符串。包括start,不包括end
3.String判断功能
1)equals(Object obj):比较字符串的内容是否相同
2)equalsIgnoreCase(String anotherString):比较字符串的内容是否相同,忽略大小写
3)startsWith(String prefix):判断字符串对象是否以指定的字符开头(区分大小写)
4)startsWith(String prefix,int toffset):判断字符串对象是否以指定的字符开头,参数toffset为指定从哪个下标开始
5)endsWith(String str):判断字符串对象是否以指定的字符结尾
6)isEmpty():判断指定字符串是否为空
7)compareTo(String anotherString):比较字符串的大小,前者大返回整数,后者大返回负数,相等返回0
4.String类中的转化方法:
1)toCharArray():把字符串转换为字符数组
2)toLowerCase():把字符串转换为小写字符串
3)toUpperCase():把字符串转换为大写字符串
5.其他常用方法
1)trim():去除字符串两端空格
2)split():去除字符串中指定的的字符,然后返回一个新的字符串
3)subSequence(int beginIndex,int endIndex ):截取字符串中指定位置的字符组成一个新的字符串
4)replace(char oldChar, char newChar):将指定字符替换成另一个指定的字符
5)replaceAll(String regex,String replasement):用新的内容替换全部旧内容
6)replaceFirst(String regex,String replacement):替换首个满足条件的内容
7)lastIndexOf(String str):返回指定字符出现的最后一次的下标
8)contains(CharSequence s):查看字符串中是都含有指定字符
9)concat(String str):在原有的字符串的基础上加上指定字符串
分析发现,类中方法主要涉及字符串的赋值、判断、拷贝等操作,实现主要涉及深浅拷贝、运算符重载等知识点。
2、实现
目前只完成了深拷贝和赋值运算符重载,其他的看情况再写
/**
* @file MyString.cpp
* @author your name (you@domain.com)
* @brief 目前只完成了深拷贝和赋值运算符重载
* @version 0.1
* @date 2022-07-10
*
* @copyright Copyright (c) 2022
*
*/
#include <string.h>
#include <iostream>
using namespace std;
class MyString
{
private:
char * m_data;//字符串
int m_size;//保存字符串长度
public:
MyString(const char *str = nullptr);//默认为空,nullptr为c++中空指针类型的关键字
MyString(const MyString & str);//构造拷贝函数
~MyString();
MyString& operator = (const MyString &str);
MyString& operator + (const MyString &str);
bool operator == (const MyString &str);
int GetLength();
void printString();
};
/**
* @brief Construct a new My String:: My String object
* 注意定义时,默认参数不要指定;
* 如果new申请失败,后面strcpy会不安全(后边的都有类似的问题)
*
* @param str
*/
MyString::MyString(const char *str)
{
if(str == nullptr){
m_data = new char[1];
m_data[0] = '\0';
m_size = 0;
}else{
m_size = strlen(str);
m_data = new char[m_size+1];
strcpy(m_data, str);//把含有'\0'结束符的字符串复制到另一个地址空间
}
}
/**
* @brief Construct a new My String:: My String object
*
* @param str
*/
MyString::MyString(const MyString & str)
{
if(str.m_data == NULL){
m_data = NULL;
m_size = 0;
}else{
m_size = strlen(str.m_data);
m_data = new char[m_size+1];
strcpy(m_data, str.m_data);//把含有'\0'结束符的字符串复制到另一个地址空间
}
}
/**
* @brief Destroy the My String:: My String object
*
*/
MyString::~MyString()
{
delete[] m_data;
m_data = NULL;
}
/**
* @brief 重载赋值运算符
*
* @param str
* @return MyString&
*/
MyString & MyString::operator =(const MyString & str)
{
if (&str != this)
{
if(m_data){//避免重复创建
/**
* @brief 对于简单的数据类型而言,delete和delete[]都只是释放内存,没有什么区别。
* 如下:int *p = new int[4];delete p;与delete p [ ];结果是一样的。
*
*/
delete[] m_data;
}
m_data = new char[str.m_size + 1];
strcpy(m_data, str.m_data);
}
return *this;
}
/**
* @brief 显示
*
*/
void MyString::printString(void)
{
cout << m_data << endl;
}
int main()
{
MyString str("hello");//等同于const char* p = "hello"
str.printString();
cout << "构造拷贝函数"<< endl;
MyString str1(str);
str1.printString();//入参的类型要一致
cout<<"重载赋值操作符"<< endl;
MyString str2;
str2 = str1;
str2.printString();
return 0;
}
3、实验现象
lynnzhang@lynnzhang-ubuntu20:~/Desktop/code/lv14_c++/day2/homework$ g++ MyString.cpp
lynnzhang@lynnzhang-ubuntu20:~/Desktop/code/lv14_c++/day2/homework$ ./a.out
hello
构造拷贝函数
hello
重载赋值操作符
hello
十三、虚函数、纯虚函数、虚析构函数
/**
* @file shape.cpp
* @author your name (you@domain.com)
* @brief 虚函数、纯虚函数、虚析构函数
* @version 0.1
* @date 2022-07-10
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>
using namespace std;
/**
* @brief 图形类
*
*/
class shape{
public:
shape(){}
/**
* @brief Destroy the shape object
* 虚析构函数,防止在派生类析构的时候没有触发基类的析构函数,导致内存泄露
*
*/
virtual ~shape(){}
//virtual double getC(void){};//声明虚函数
/**
* @brief 声明为纯虚函数,不能用这个类定义对象
*
* @return double
*/
virtual double getC(void) = 0;
private:
};
/**
* @brief 圆形,Cir类继承shape类
*
*/
class Cir: public shape{
public:
Cir(double ri):r(ri){//默认初始化表,只有构造函数可以这么做
}
double getC(void)
{
return 2*3.14*r;
}
private:
int r;
};
/**
* @brief 三角形
*
*/
class Tri:public shape{
public:
Tri(double a, double b, double c):e1(a),e2(b),e3(c)
{
}
double getC(void)
{
return e1+e2+e3;
}
private:
double e1;
double e2;
double e3;
};
/**
* @brief 正方形
*
*/
class Rec: public shape{
public:
Rec(double e)
{
this->e = e;
}
double getC(void)
{
return 4*e;
}
private:
double e;
};
/**
* @brief 统计不同图形的周长和
*
* @param arr 基类的指针指向派生类使没有问题的
* @param n
* @return double
*/
double countC(shape *arr[], int n)
{
double sum = 0;
for(int i=0; i<n; i++)//遍历所有的图形
{
sum += arr[i]->getC();//每次获取到的都是当前图形类下的getC方法(因为基类同名方法为虚函数)
}
return sum;
}
int main()
{
Cir c(1);
Rec r(3);
Cir c1(2);
Tri t(3,3,3);
shape *arr[] = {&c, &r, &c1, &t};
cout << "total C: "<<countC(arr, 4) << endl;
}
十四、异常
1、定义
1、 异常,语法
int atoi(constchar *ptr);
int myatoi(constchar *ptr)
{
if()//不是数字字母
throw myexceprion(“!0-9”);
……
}
调用:
try{
num= myatoi(“abcd”);
}
catch(myexception&e){
cout<<e.what();
}
2. Exception类
class exception //namespace std
{
public:
exception() throw() { }//成员函数声明后面跟上throw(),表示告诉类的使用者:
我的这个方法不会抛出异常,所以,在使用该方法的时候,
不必把它至于 try/catch 异常处理块中。
virtual ~exception() throw();//不抛异常函数
//Returns a C-style character string describing the general cause of the current error.
virtual constchar* what() constthrow();
};
3. 自定义异常类
class myexception: public exception
{
public:
constchar* what() constthrow()
{
return “xxxxxxxxxx”;
}
};
2、实例
#include <iostream>
#include <stdlib.h>
#include <exception>
using namespace std;
/**
* @brief 参数异常类
*
*/
class argexception:public exception
{
public:
/**
* @brief 声明为不抛异常函数
*
* @return const char*
*/
const char *what() const throw()
{
return "arg Err!";
}
};
int myatoi(const char *str)
{
if(*str < '0' || *str > '9'){
throw argexception();
}else{
return atoi(str);
}
}
int main()
{
try{
int data = myatoi("abd");
cout << data << endl;
}
catch(argexception e){
cout << e.what() << endl;
}
}
十五、标准类型转换、自定义类型转换、避免隐式类型转换
1、标准类型转换
/**
* @file cast.cpp
* @author your name (you@domain.com)
* @brief 转换函数
* @version 0.1
* @date 2022-07-10
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>//cpp里面头文件没有后缀名
#include <typeinfo>
using namespace std;
class A{
public:
virtual void show()
{
cout<<"aaaaaaa"<<endl;
}
};
class B:public A{
public:
void show()
{
cout<<"bbbbbbbbbbbbaaaaaaa"<<endl;
}
};
int main()
{
#if 0
int a;
// char *p = (char*)&a;//c中的强转方式
char *p = reinterpret_cast<char *>( &a );//c++的强转方式1,用的少
#endif
#if 0
const int b = 100;
int *q = const_cast<int*>( &b );//c++的强转方式2:把一个常量转换为其他非常量,用的少
#endif
#if 0
A a;
B &p = static_cast<B &>( a );//c++的强转方式3:有继承关系的类的转换
#endif
try{
A a;
B &p = dynamic_cast<B &>( a );c++的强转方式4:检测转换的合法性
}
catch(bad_cast e)//bad_cast是一个标准异常类,通过grep bad_cast /usr/include/ -r 搜索头文件
{
cout<<e.what()<<endl;
}
}
2、自定义类型转换
在前面mytimer类中增加:
/**
* @brief 自定义整型转换
*
* @return int
*/
operator int()
{
return sec+min*60+hour*60*60;
}
int sec = t;//自定义类型转换
cout<< sec <<endl;
3、避免隐式类型转换
/**
* @file mempool.cpp
* @author your name (you@domain.com)
* @brief explicit关键字,声明不能发生相应的隐式类型转换
* @version 0.1
* @date 2022-07-10
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>
using namespace std;
class mempool{
public:
/**
* @brief Construct a new mempool object
* 声明不能发生相应的隐式类型转换,避免不必要的类型转换
* @param size
*/
explicit mempool(int size){
data = new char[size];
cout << size << endl;
}
~mempool()
{
delete [] data;
}
private:
char *data;
};
int main()
{
// mempool a(100);
mempool a = 100;//增加了explicit声明会在不合理的强转时报错
}
十六、模板
1、类型模板
/**
* @file arr.cpp
* @author your name (you@domain.com)
* @brief 类型模板
* @version 0.1
* @date 2022-07-11
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>
using namespace std;
template <typename XXX>//声明一个模板
/**
* @brief 模板类
*
*/
class ARR{
public:
ARR():tail(0){
}
void addtail(XXX data);
void show(void);
private:
XXX data[100];
int tail;
};
template <typename XXX>
void ARR<XXX>::addtail(XXX data)
{
this->data[tail++] = data;
}
template <typename XXX>
void ARR<XXX>::show(void)
{
int i = 0;
for(;i<tail; i++)
cout<< data[i] <<',';
cout<<endl;
}
int main()
{
ARR<int> arr;
arr.addtail(1);
arr.addtail(2);
arr.addtail(3);
arr.addtail(4);
arr.show();
ARR<double> arr1;
arr1.addtail(1.1);
arr1.addtail(22.3);
arr1.addtail(3.5);
arr1.addtail(4.9);
arr1.show();
}
2、非类型模板
/**
* @file arr.cpp
* @author your name (you@domain.com)
* @brief 非类型模板
* template <class T, intSIZE>
* class List{
* public:
* List();
* ~List();
* private:
* T arr[SIZE];
* int num;
* };
* @version 0.1
* @date 2022-07-11
*
* @copyright Copyright (c) 2022
*
*/
#include <iostream>
using namespace std;
template <typename XXX, int SIZE>
class ARR{
public:
ARR():tail(0){
}
void addtail(XXX data);
void show(void);
private:
XXX data[SIZE];
int tail;
};
template <typename XXX, int SIZE>
void ARR<XXX, SIZE>::addtail(XXX data)
{
this->data[tail++] = data;
}
template <typename XXX, int SIZE>
void ARR<XXX, SIZE>::show(void)
{
int i = 0;
for(;i<tail; i++)
cout<< data[i] <<',';
cout<<endl;
}
int main()
{
ARR<int, 100> arr;
arr.addtail(1);
arr.addtail(2);
arr.addtail(3);
arr.addtail(4);
arr.show();
ARR<double, 1000> arr1;
arr1.addtail(1.1);
arr1.addtail(22.3);
arr1.addtail(3.5);
arr1.addtail(4.9);
arr1.show();
}
3、模板特化
/**
* @file template.cpp
* @author your name (you@domain.com)
* @brief 特化模板
* @version 0.1
* @date 2022-07-11
*
* @copyright Copyright (c) 2022
*
*/
#include <stdio.h>
#include <iostream>
using namespace std;
#if 0
int add(int a, int b)
{
return a+b;
}
double add(double a, double b)
{
return a+b;
}
#endif
template<typename XXX>
/**
* @brief 模板函数
*
* @param a
* @param b
* @return XXX
*/
XXX add(XXX a, XXX b)
{
return a+b;
}
/**
* @brief 模板特化
*
* @tparam
* @param a
* @param b
* @return true
* @return false
*/
template <>//特化模板声明,写不写都没问题
bool add(bool a, bool b)
{
if(a == true && b == true)
return true;
return false;
}
int main()
{
cout<< add(1, 2) <<endl;
cout<< add(1.1, 2.3) <<endl;
cout<< add(true, false) <<endl;
cout<< add(true, true) <<endl;
}