C++ - 一种特殊情况的类结构设计

  现在,假想需要设计一个 JSON 类型数据结构,善于提前构思的你,大致想了想:

Object {
    "key": Boolean,
    "key": Number,
    "key": String,
    "key": Array,
    "key": Object
}

  嗯,看起来没什么问题,开始设计...没写多久,可能考虑到这种情况:

Object {
    "key": [Object{}, Object{}, ...]
}

  这意味着类结构大概变成这样:

class Object {
    bool boolean;
    double number;
    std::string string;
    std::vector<Object> array;
    Object object;
}

  当然,你很明白 Object 是一个多键值对,于是改一下:

class Value {
public:
    bool boolean;
    double number;
    std::string string;
    std::vector<Object> array;
    Object object;
};

class Object {
public:
    std::vector<std::pair<std::string key, Value value>>> objects;
}

  这是一个互相包含的类,对于 C++ 而言,显然编译器会报错,因为 Value 类中使用了未定义的 Object 类型,而此处编译器无法确定你的 Value 类成员变量的大小。

  不过,你还是有办法,使用指针,并向前声明 Object 类:

class Object;

class Value {
public:
    bool boolean;
    double number;
    std::string string;
    std::vector<Object> array;
    Object* object;
};

class Object {
public:
    std::vector<std::pair<std::string, Value>>> objects;
}

  可是,这时又会有个小问题,如果你想要像下面这样使用接口

Object object;

object["Window"] = Value(Object("Color", Value("blue")));
std::cout << object["Window"].GetObject()["Color"].GetString();

  是不可行的,因为 Value 中 object 是指针,当重载 [] 运算符后,只能这样使用它:

Object object;

object["Window"] = Value(new Object("Color", Value("blue")));
std::cout << object["Window"].GetObject()->operator[]("Color").GetString();

  看起来很怪异,如果将就着用倒也没什么,但换一个位置会好些:

class Value;

class Object {
public:
    std::vector<std::pair<std::string, Value*>>> objects;
}

class Value {
public:
    bool boolean;
    double number;
    std::string string;
    std::vector<Object> array;
    Object object;
};

  用法变为:

Object object;

object["Window"] = new Value(Object("Color", Value("blue")));
std::cout << object["Window"]->GetObject()["Color"]->GetString();

  如果想彻底去掉包含指针这个问题需要做一些工作,通过中间类继承 + friend class 来去掉可以做到十分完美的样子,参见微软开源的 cpprestsdk json 代码实现。

  当然,或许还有其他办法解决这个问题,但我觉得最优美且最能体现 C++ 风格的方法就是继承 + 友元类。

  这篇博客仅仅想写下当初写 JSON 构造 / 分析器时遇到的这个类结构设计问题,尽管这里只提到了向前类声明的使用,实际上,除开数字值的类型问题(int64 -> int32 -> double 溢出问题),性能问题,当时还学到了很多 C++ 的用法。(以后可能会较少用 C++ 了)

  最后给一个极简的 JSON 构造器实现:

#include <iostream>
#include <vector>
#include <string>

class Value;

class Boolean {
private:
	bool _boolean;
public:
	Boolean()
		: _boolean() {}
	Boolean(bool val)
		: _boolean(val)
	{

	}

	bool GetVal() const {
		return _boolean;
	}

	void SetVal(bool val)
	{
		_boolean = val;
	}
};

class Number {
private:
	double _number;
public:
	Number()
		: _number() {}
	Number(int val)
		: _number(val)
	{

	}
	Number(double val)
		: _number(val)
	{

	}

	double GetVal() const {
		return _number;
	}

	void SetVal(double val) {
		_number = val;
	}
};

class String {
private:
	std::string _string;
public:
	String() {}
	String(const char* val)
		: _string(val)
	{

	}
	String(std::string val)
		: _string(val)
	{

	}
	std::string GetVal() const {
		return _string;
	}

	void SetVal(std::string val)
	{
		_string = val;
	}
};

class Array {
private:
	std::vector<Value> _array;
public:
	Array()
		: _array()
	{
	}
	Array(std::vector<Value> val)
		: _array(val)
	{

	}
	std::vector<Value> GetVal() const
	{
		return _array;
	}

	void SetVal(std::vector<Value> val)
	{
		_array = val;
	}
};


class Object {
	std::vector<std::pair<std::string, Value*>> _elements;
private:
	typedef std::vector<std::pair<std::string, Value*>>::iterator iterator;

	iterator find_iter_by_key(std::string key)
	{
		return std::find_if(_elements.begin(), _elements.end(), [&key](const std::pair<std::string, Value*>& p) { return p.first == key; });
	}

public:
	Object() {}
	Object(std::string key, Value* value)
		: _elements{ {key, value} }
	{

	}

	Value*& operator[](std::string key)
	{
		return GetValue(key);
	}

	size_t Size() const
	{
		return _elements.size();
	}

	Value*& GetValue(std::string key)
	{
		auto iter = find_iter_by_key(key);
		if (iter == _elements.end())
			return _elements.insert(iter, std::pair<std::string, Value*>(key, nullptr))->second;
		return iter->second;
	}
};

class Value {
	Boolean v_boolean;
	Number v_number;
	String v_string;
	Array v_array;
	Object v_object;

public:
	Value()
		: v_boolean(), v_number(), v_string(), v_array(), v_object()
	{
	}

	Value(const Value & value)
	{
		*this = value;
	}

	Value(bool value)
		: v_boolean(value), v_number(), v_string(), v_array(), v_object()
	{

	}
	Value(int value)
		: v_boolean(), v_number(value), v_string(), v_array(), v_object()
	{

	}
	Value(double value)
		: v_boolean(), v_number(value), v_string(), v_array(), v_object()
	{

	}
	Value(std::string value)
		: v_boolean(), v_number(), v_string(value), v_array(), v_object()
	{

	}
	Value(std::vector<Value> value)
		: v_boolean(), v_number(), v_string(), v_array(value), v_object()
	{
	}
	Value(Boolean value)
		: v_boolean(value), v_number(), v_string(), v_array(), v_object()
	{

	}
	Value(Number value)
		: v_boolean(), v_number(value), v_string(), v_array(), v_object()
	{

	}
	Value(String value)
		: v_boolean(), v_number(), v_string(value), v_array(), v_object()
	{

	}
	Value(Array value)
		: v_boolean(), v_number(), v_string(), v_array(value), v_object()
	{

	}
	Value(Object value)
		: v_boolean(), v_number(), v_string(), v_array(), v_object(value)
	{

	}

	bool GetBoolean() const
	{
		return v_boolean.GetVal();
	}

	double GetNumber() const
	{
		return v_number.GetVal();
	}

	int GetIntNumber() const {
		return (int)v_number.GetVal();
	}

	std::string GetString() const
	{
		return v_string.GetVal();
	}

	std::vector<Value> GetArray() const
	{
		return v_array.GetVal();
	}

	Object GetObject() const
	{
		return v_object;
	}

	Value& GetValue()
	{
		return *this;
	}


};

int main()
{
	Object object("test", new Value(Object("abc", new Value(123))));
	object["color"] = new Value(Array{ std::vector<Value>{ Value(String("red")), Value(String("orange")), Value(String("yellow")) } });
	std::cout << object["test"]->GetObject()["abc"]->GetIntNumber() << '\n';
	for (auto i : object["color"]->GetArray())
		std::cout << i.GetString() << " ";
	return 0;
}

  输出:

123
red orange yellow

  

猜你喜欢

转载自www.cnblogs.com/darkchii/p/12563533.html
今日推荐