C/C++经验总结

一、易错的地方

1. 排序问题

a 正整数从小到大排序和转化成字符串按字典顺序排序是不一样的。

2. 语法问题

a 移位运算符的优先级低于算数运算符,即左移和右移<<、>>运算符要低于±运算符,在使用移位运算符时往往需要添加括号。
b 将char类型数字在表达式中运算时容易产生错误,字符1代表的值并不是1而是49。
c vector容器不支持pop_front,如果需要支持首部元素的删除可以deque双端队列。

二、高级用法

1. 字符串处理

1.1 字符串和整数的相互转换

a 从整数转换成字符串:str=to_string(int)。
b 从字符串转换成数字:int=stoi(str)。
c 如果遇上浮点数或者更加复杂的数据类型,可以使用sstream中的stringstream完成转换。
d C++中两个类不能相互定义为实体,但可以类A使用类B的实体,类B使用类A的引用或指针。类的申明称为前向申明,前向申明类后可以将其用为引用或者指针。

 double x=1.234;
 string str;
 stringstream stream;//istringstream和ostringstream从stringstream派生
 stream<<x;
 stream>>str;

1.2 getline的高级用法

当使用C++处理字符串时可能没有python那么方便,不过库函数的灵活运用,确实会在一些追求效率的场景很有意义。getline(cin,str,‘.’)表示从cin流对象中按’.'分割,读取字符串到str。getline默认以换行符为分隔,不过可以自定义分隔符号,很好用。

1.3 子字符串

a 从第i个字符开始的n个字符。
sub_str=str.substr(i,n);
b 查找一个字符串是不是另一字符串的子串,不像容器那样返回迭代器,如果找到了返回子字符串出现的起始位置,如果没有找到返回字符串的长度,即最后一个有效的下标加1。
str.find(sub_str)

2. 特殊关键字的使用

2.1 static关键字的使用

用法:
a 修饰全局变量,改变其作用域,该变量只能在本文件中使用,起到保护变量的作用,防止被自己或者其他开发者意外访问。
b 函数申明为static,改变其作用域,只能在文件内部调用该函数,当开发者不期望在文件外函数被意外访问时,可以这样使用。
c 函数内的变量申明为static,改变其生存期,使得局部变量在函数的整个运行期都存在。虽然这种用法功能很强大,但一般不要使用,因为这使得函数第一次调用和往后调用的行为不一样。对于需要使用很多静态变量才能解决的问题,可以尝试使用类代替。
d 在类中申明静态变量,该变量不再属于对象而是属于类。在头文件中申明该变量,在cpp文件中定义并初始化该变量。曾用于统计该类创建了多少个实例。
e 类成员函数也可申明为static,但尚没找到为什么要这么做的原因。
总而言之,用法a、b、d在适当的时候可以使用,其余用法不推荐使用。

2.2 const关键字使用

a 指向const对象的指针,意味着对象不允许修改,可用于只读对象。

const char* p;

b 函数的形参申明为const,当函数形参为指针或者引用时,实参可能会被被调函数修改,当不需要这种行为时,通过const禁用这种行为。
c 函数的形参申明为const与非const可构成重载,叫做基于const的重载。
d 在重载操作符时,往往将形参设置为const,这样可以与内置类型保持一致,从而可以继承内置类型的结合性,以实现运算符混合使用。
e const还可以修饰成员函数,const对象只能调用const成员函数,而const成员函数可以被非const对象调用。如果成员函数实现的功能不需要对对象进行修改,那么申明为const总是好的。在设计成员函数时,就要意识到这个成员函数对于对象是一种只读模式还是写模式。
class sales_item{
public:
bool same_isbn(const sales_item &rhs) const{}
}

2.3 const_cast

const_cast可以修改常量。其方法是将常量的指针或者引用转为非const类型,然后进行修改。但值得注意的是。编译器会对初始化为立即数或者字符字面值的常量进行替换,要知道这种奇怪的行为。

三、类的设计

1. 定义为成员还是派生

书里说类的派生不是为了继承,而是为了动态绑定,怎么设计类的层次?在此也没有完整的答案,只能摸着石头过河,先记录一些案例。
何为动态绑定?基类函数指针或者引用调用的方法根据实际绑定的对象而定,基类指针绑定了何种对象就调用何种方法。当派生新增了一些功能时,基类指针能调用到新的功能,这样看来动态绑定适合于不断添加新功能的场景。
如下图,展示了一个书的分类层次。自然而然,书可以作为基类,理工类、农学类等可以作为第一层的派生类,具体学科物理学、数学作为第二层派生类。但这种类的层次设计能否很好的支持功能的实现呢,笔者认为得根据具体的情况而定。假设开了一间书店,一位顾客买了3本书,要计算总价。试想都可以通过get_price()获取价格,每本书对应的具体的get_price()可能因为折扣等不同,这些差异动态绑定可以很好的处理。因此,类的动态绑定适合于这种需要的抽象操作相同(获取价格、查询库存等),但是具体实现又有不同的场景。
试想另外一种场景,一个是书店老板卖书,一个是印刷厂印书。是否适合把这两个设计到类的继承层次中?印书厂老板想着自己的书是没有缺页漏页的程式,而书店老板则是要计算总价,把这两个糅合到一起失去了类设计的目的“动态绑定”。当然,我会说,糅合到一起可以继承书的一些共性,那用的是类的继承而不是动态绑定。当想用继承这种特性时,把书定义为新类的一个数据成员更好。

理工类
农业类
医学类
人文社科类
物理学
数学
...
...

2. 矩阵计算中加法是否适合派生

如下Matrix类中定义加法,那么是否适合定义一个向量类Vector作为Matrix的子类来继承该操作,或者为Matrix和Vector定义一个共同的模板基类来减少代码重复。然而加法操作返回为该类型的对象,若采用“类Vector作为Matrix的子类”,那么当两个向量相加赋给第3个向量时就要完成Matrix类到Vector类的转换。这种转换虽然可以定义,但是这样写会造成难以理解。
那么,是否可以为矩阵和向量定义一个共同的模板基类呢?肯定是可以的,可是这模板如何设计还得继续深究。

class Matrix
{
public:
	Matrix operator+(const Matrix &);
}

3. 类的行为

一个类里面的成员函数可以直接在类里被调用,就像调用普通函数一样。

四、常用数据结构实现

1. vector

C++中的vector为实现数组的动态增长提供了便利,然而有时候在C语言中不想移值STL库,那么可以尝试自己实现一些基本功能。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define STR_MAX 100

typedef struct
{
    int start_time;
    int end_time;
    char name[STR_MAX];
} Task;

typedef struct
{
    int size;
    int contain;
    Task *arr;
} Tasks;

void SetTask(Task *des, Task sou)
{
    des->start_time = sou.start_time;
    des->end_time = sou.end_time;
    strcpy(des->name, sou.name);
}

void PushBack(Tasks *tasks, Task task)
{
    if (tasks->contain > tasks->size)
        SetTask(&(tasks->arr[tasks->size++]), task);

    else
    {
        tasks->contain *= 2;
        Task *new_task = (Task *)malloc(sizeof(Task) * tasks->contain);
        for (int i = 0; i < tasks->size; i++)
            SetTask(&(new_task[i]), tasks->arr[i]);
        free(tasks->arr);
        tasks->arr = new_task;
        SetTask(&(tasks->arr[tasks->size++]), task);
    }
}

int main()
{
    Task task = {
        .start_time = 1,
        .end_time = 10,
        .name = "task1"};

    Tasks tasks = {
        .size = 0,
        .contain = 1,
        .arr = (Task *)malloc(sizeof(Task))};

    PushBack(&tasks, task);
    PushBack(&tasks, task);
    PushBack(&tasks, task);
    printf("tasks.contain:%d\ttasks.size:%d\n", tasks.contain, tasks.size);
    for (int i = 0; i < tasks.size; i++)
    {
        printf("start_time:%d\tend_time:%d\tname:%s\n",
               tasks.arr[i].start_time, tasks.arr[i].end_time, tasks.arr[i].name);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值