【C++】C++基础

目录

一、编译命令

1、实例

二、引用

1、定义

2、创建与使用

3、实例

4、指针和引用的区别

三、函数重载

1、定义

2、实例

四、默认参数

1、定义

2、创建与使用

3、实例

五、堆内存

1、定义

2、创建与使用

3、delete和delete[]的区别

六、类与对象

1、定义

2、创建与使用

七、友元

1、定义

2、创建与使用

示例:实现顺序表

1、设计

2、实现(目前只支持可显的一位字符)

SqList.h

SqList.cpp

main.app

编译执行

实验现象

八、深浅拷贝

1、构造拷贝函数

2、赋值运算符重载

3、实例

九、运算符重载

十、仿函数重载

十一、输入输出

十二、类的继承和重载

1、继承权限

2、proteced和private的区别

示例:模仿c++的string类,自己实现string类

1、设计

2、实现

3、实验现象

十三、虚函数、纯虚函数、虚析构函数

十四、异常

1、定义

2、实例

十五、标准类型转换、自定义类型转换、避免隐式类型转换

1、标准类型转换

2、自定义类型转换

3、避免隐式类型转换

十六、模板

1、类型模板

2、非类型模板

3、模板特化


一、编译命令

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值