Tiny-JSON 项目记录

前言

Tiny-JSON 项目过程中所遇到的问题,做一个简单的整理。

反思

在项目开发过程中,主要采取的方法是自底向上的,忽略了顶层设计,而直接着手于具体的细节。这在项目开发过程中带来了一定的麻烦,导致时常需要重新设计或调整系统结构。

一方面来说,由于顶层设计依赖于底层的实现细节,采取自顶向下会有种无从下手的感觉,而对于详细的细节,不管是基础类的实现还是测试,都可以很有头绪的完成。但弊端就是基础类的设计对于整体系统来说,并不一定是最合适的,在后续的整合中,时常需要调整基础类的设计和实现。

另一方面,对这个系统的功能需求还不明确,许多功能在开发过程中才明确需要,这也就导致边开发边调整,需要来来回回修改甚至重构。由于本项目规模较小,修改和重构的代价是可以接受的,但如果项目规模较大,时间成本可能是无法接受的。

所以,在项目开始阶段,应该首先明确顶层设计和整体架构,这些类的具体功能并不需要马上实现,只需要有个简单的测试输出即可。然后在后续的开发阶段,补全这些具体功能,完成单元测试,最后在整合过程中进行集成测试。

switch 中的变量

switchcase 中定义变量,报 crosses initialization of 'xxx'

原因:在 case 中定义变量,如果没有大括号,则所有 case 都能访问得到,但其只在该 case 中进行了初始化,在其他 case 中访问的值是未定义的,所以报错。

解决方法:用 { } 包裹 case,限定其变量的作用域。

类中成员的默认参数

在类中,默认参数要在定义函数时就写全,在实现中无需写出,否则报错,即使值一样

例:

class Test{
public:
	void fcn(int, int = 2);
}

// 报错 default argument given for parameter 2 of 'void Test::fcn(int, int)'
void Test::fcn(int a, int b = 2){
	...
}
// 正确,b的缺省默认值为 2
void Test::fcn(int a, int b){
	...
}

命名空间中的变量

在命名空间中,直接定义变量会导致重复定义,报错 redefinition of 'xxx'

解决方式:在头文件只声明,在源文件定义

// .h
namespace name {
	extern bool DEBUG;
}

// .cpp
bool name::DEBUG = false;

内部函数与外部函数

内部函数:只有该文件内能够调用(默认)
外部函数:该文件和其他文件都能调用

如果没有任何修饰符,源文件中的函数默认是内部函数,外部无法调用

声明外部函数:

// .h
extern void fcn();

// .cpp
void fcn(){
	...
}

显式声明内部函数:

// .cpp
static void fcn(){
	...
}

定义模板的别名

使用 using 关键字:

template <typename T> using ptr = std::unique_ptr<T>;

ptr<SomeClass> ptr1;

JSON 中的数据格式

字符串中的 \ 需要再添加一个 \ 进行转义,例如 \ -> \\
数字支持 1.23e+12 的科学计数法格式

委托构造函数

构造函数要进行委托时,放在参数列表后,不要放在函数体内,否则会产生错误

在构造函数内的 Test(...) 并不是期望中的委托构造函数,而是生成一个 Test 的临时对象

例如:

class Test{
public:
    Test(int);
    Test(double);
    int i = 0;
};

Test::Test(int i): i(i){}
// 错误写法
Test::Test(double d){
    Test(static_cast<int>(d));
}

// 正确写法
Test::Test(double d):Test(static_cast<int>(d)){}

int main(){
    double d = 1.1;
    int i = 2;
	Test a(d);  // 如果是错误写法,i 的实际值为 0
    Test b(i);
  
    return 0;
}

智能指针

  • 对于一个对象,不要同时使用 shared_ptrunique_ptr,智能指针销毁析构,会对原对象也进行析构,产生悬空指针。要一起使用 shared_ptrunique_ptr 只能是 unique_ptr 放弃所有权,交由 shared_ptr 管理。
  • 在类中不要使用 this 初始化一个智能指针,智能指针离开作用域就会析构 this,导致悬空指针。只有类继承 enable_shared_from_this 再使用 shared_from_this() 才能用 this 初始化一个智能指针

在对象或方法内部使用时优先使用 unique_ptr
一个对象需要被多个 class 同时使用的时候用 shared_ptr
当出现循环引用时,一方使用 weak_ptr

make_sharedmake_unique 方法,都会重新构造一个对象,原对象析构不影响。

智能指针的转化 dynamic_pointer_cast

C++ 字符串的坑

在参数类型中,如果重载函数参数列表是:

void fcn(bool);
void fcn(string&);

此时执行 fcn("abc") 会调用 fcn(bool)
必须要再声明一个重载函数 fcn(char[]) 才能按预期调用,因为 "abc" 实际上是 char[] 而不是字符串,需要一步隐式转化,而匹配 bool 并不需要隐式转化。

union 的坑(Segmentation Fault)

union{
	shared_ptr<xx> mem1;
    shared_ptr<xx> mem2;
}

mem2 = make_shared<xx>(); // occurs Segmentation Fault

stackoverflow 上有一个相关的帖子,说的是当 = 不是 trivial 时,就会错误,而类型的赋值都不是 trivial 的,所以 union 不能放类,只能放指针或基础类型。

typename 和 class

在模板声明中,一般情况下,typenameclass 没有区别,但涉及到模板推导时,需要使用 typename 显式告诉编译器这是个类型,而 class 无法做到这一点。

上一篇 下一篇

评论 | 0条评论