1.JSON(JavaScript Object Notation)一种简单的数据格式,比xml更轻巧.JSON是JavaScript原生格式,这意味着在JavaScript中处理JSON数据不需要任何特殊的API或工具包
。2.JSON的规则很简单:
。对象是一个无序的“ '名称/值'对”集合一个对象以“{”(左括号)开始,“}”(右括号)结束每个“名称”后跟一个“:” (冒号);“'名称/值'对”之间使用“,”(逗号)分隔“
。3.规则如下:
1)数值之间的数值
之间用逗号(“,”)分隔。名称1:值1,名称2:值2
3)映射的集合(对象)用大括号(“{}”)表示。{名称1:值1,名称2:值2}
4)并列数据的集合(数组)用方括号(“[]”)表示。
[
{名称1:值,名称2:值2},
{名称1:值,名称2:值2}
]
5元素值可具有的类型:string,number,object,array,true,false,null
4.下面写一个简单的json示例,方便理解
[javascript] v 查看纯文本
<script type =“text / javascript”>
<! -
window.onload = function(){
//对象 集合|数组
var obj = {
"name":"redarmy",
"age":10,
toString:function(a,b){
alert("你好"+(a+b));
return a+b;
}
};
//数组
var users = [{
"name":"redarmy1",
"age":10
},
{
"name":"redarmy2",
"age":10
},
{
"name":"redarmy3",
"age":10
}];
alert(obj.toString(1,2));
/* for(var i=0;i<users.length;i++){
alert(users[i].name);
} */
}
//-->
</script>
5.简单总结一下json
优点:
1)作为一种数据传输格式,JSON 与 XML 很相似,但是它更加灵巧。
2)JSON 不需要从服务器端发送含有特定内容类型的首部信息。
缺点:
1)语法过于严谨
2)代码不易读
3)eval 函数存在风险
JSON 语法是 JavaScript 语法的子集。
JSON 语法规则
JSON 语法是 JavaScript 对象表示法语法的子集。
数据在名称/值对中
数据由逗号分隔
花括号保存对象
方括号保存数组
JSON 名称/值对
JSON 数据的书写格式是:名称/值对。
名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值:
"firstName" : "John"
这很容易理解,等价于这条 JavaScript 语句:
firstName = "John"
JSON 值
JSON 值可以是:
数字(整数或浮点数)
字符串(在双引号中)
逻辑值(true 或 false)
数组(在方括号中)
对象(在花括号中)
null
JSON 对象
JSON 对象在花括号中书写:
对象可以包含多个名称/值对:
{ "firstName":"John" , "lastName":"Doe" }
这一点也容易理解,与这条 JavaScript 语句等价:
firstName = "John"
lastName = "Doe"
JSON 数组
JSON 数组在方括号中书写:
数组可包含多个对象:
{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}
在上面的例子中,对象 "employees" 是包含三个对象的数组。每个对象代表一条关于某人(有姓和名)的记录。
JSON 使用 JavaScript 语法
因为 JSON 使用 JavaScript 语法,所以无需额外的软件就能处理 JavaScript 中的 JSON。
通过 JavaScript,您可以创建一个对象数组,并像这样进行赋值:
例子
var employees = [
{ "firstName":"Bill" , "lastName":"Gates" },
{ "firstName":"George" , "lastName":"Bush" },
{ "firstName":"Thomas" , "lastName": "Carter" }
];
可以像这样访问 JavaScript 对象数组中的第一项:
employees[0].lastName;
返回的内容是:
Gates
可以像这样修改数据:
employees[0].lastName = "Jobs";
typedef 函数指针的用法
进入正文:
代码简化, 促进跨平台开发的目的.
typedef 行为有点像 #define 宏,用其实际类型替代同义字。
不同点:typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。
用法一:
typedef int (*MYFUN)(int, int);
这种用法一般用在给函数定义别名的时候
上面的例子定义MYFUN 是一个函数指针, 函数类型是带两个int 参数, 返回一个int
在分析这种形式的定义的时候可以用下面的方法:
先去掉typedef 和别名, 剩下的就是原变量的类型.
去掉typedef和MYFUN以后就剩:
int (*)(int, int)
用法二:
typedef给变量类型定义一个别名.
typedef struct{
int a;
int b;
}MY_TYPE;
这里把一个未命名结构直接取了一个叫MY_TYPE的别名, 这样如果你想定义结构的实例的时候就可以这样:
MY_TYPE tmp;
第二种用法:typedef 原变量类型 别名
简单的函数指针的用法
//形式1:返回类型(*函数名)(参数表)
char(*pFun)(int);
//typedef char(*pFun)(int) //跟上一行功能等同
/*typedef的功能是定义新的类型。第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。*/
char glFun(int a){return;}
void main()
{
pFun =glFun;
(*pFun)(2);
}
第一行定义了一个指针变量pFun.它是一个指向某种函数的指针,这种函数参数是一个int类型,返回值是char类型。只有第一句我们还无法使用这个指针,因为我们还未对它进行赋值。
第二行定义了一个函数glFun().该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数-函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址。
下面是一个例子:
C代码 复制代码
//#include<iostream.h>
#include<stdio.h>
typedef int (*FP_CALC)(int, int);
//注意这里不是函数声明而是函数定义,它是一个地址,你可以直接输出add看看
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return b? a/b : -1;
}
//定义一个函数,参数为op,返回一个指针。该指针类型为 拥有两个int参数、
//返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址
FP_CALC calc_func(char op)
{
switch (op)
{
case '+': return add;//返回函数的地址
case '-': return sub;
case '*': return mul;
case '/': return div;
default:
return NULL;
}
return NULL;
}
//s_calc_func为函数,它的参数是 op,
//返回值为一个拥有 两个int参数、返回类型为int 的函数指针
int (*s_calc_func(char op)) (int, int)
{
return calc_func(op);
}
//最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果
int calc(int a, int b, char op)
{
FP_CALC fp = calc_func(op); //根据预算符得到各种运算的函数的地址
int (*s_fp)(int, int) = s_calc_func(op);//用于测试
// ASSERT(fp == s_fp); // 可以断言这俩是相等的
if (fp) return fp(a, b);//根据上一步得到的函数的地址调用相应函数,并返回结果
else return -1;
}
void main()
{
int a = 100, b = 20;
printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));
printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));
printf("calc(%d, %d, %c) = %d\n", a, b, '*', calc(a, b, '*'));
printf("calc(%d, %d, %c) = %d\n", a, b, '/', calc(a, b, '/'));
}
//#include<iostream.h>
#include<stdio.h>
typedef int (*FP_CALC)(int, int);
//注意这里不是函数声明而是函数定义,它是一个地址,你可以直接输出add看看
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return b? a/b : -1;
}
//定义一个函数,参数为op,返回一个指针。该指针类型为 拥有两个int参数、
//返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址
FP_CALC calc_func(char op)
{
switch (op)
{
case '+': return add;//返回函数的地址
case '-': return sub;
case '*': return mul;
case '/': return div;
default:
return NULL;
}
return NULL;
}
//s_calc_func为函数,它的参数是 op,
//返回值为一个拥有 两个int参数、返回类型为int 的函数指针
int (*s_calc_func(char op)) (int, int)
{
return calc_func(op);
}
//最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果
int calc(int a, int b, char op)
{
FP_CALC fp = calc_func(op); //根据预算符得到各种运算的函数的地址
int (*s_fp)(int, int) = s_calc_func(op);//用于测试
// ASSERT(fp == s_fp); // 可以断言这俩是相等的
if (fp) return fp(a, b);//根据上一步得到的函数的地址调用相应函数,并返回结果
else return -1;
}
void main()
{
int a = 100, b = 20;
printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));
printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));
printf("calc(%d, %d, %c) = %d\n", a, b, '*', calc(a, b, '*'));
printf("calc(%d, %d, %c) = %d\n", a, b, '/', calc(a, b, '/'));
}
运行结果
calc(100, 20, +) = 120
calc(100, 20, -) = 80
calc(100, 20, *) = 2000
calc(100, 20, /) = 5
十一、类的private/protected/public属性
1、类的成员如果没有指定访问域,默认是private的。
2、标识符protected 与 private类似,它们的唯一区别在继承时才表现出来。当定义一个子类的时候,基类的protected 成员可以被子类的其它成员所使用,然而private 成员就不可以。
3、public/protected/private继承的区别:
(1)public继承:父类的public依然是public,protected依然是protected,private不可访问;
(2)protected继承:父类的public称为protected,protected称为private;
(3)private继承:父类的所有成员全部变成private。
十二、关于空类
编译器为一个空类提供哪些默认函数?
1、C++编译器会提供默认的构造函数,析构函数, 拷贝构造函数和拷贝赋值操作符(请参考著名的Effective C++的第三版的第5条)
当我们定义一个class而没有明确定义构造函数的时候,编译器会自动假设两个重载的构造函数 (默认构造函数"default constructor" 和复制构造函数"copy constructor")。拷贝构造函数是一个只有一个参数的构造函数(原型:ClassName(ClassName &cn){};),该参数是这个class的一个对象,这个函数的功能是将被传入的对象(object)的所有非静态(non-static)成员变量的值都复制给自身这个object。
必须注意:这两个默认构造函数(empty construction 和 copy constructor )只有在没有其它构造函数被明确定义的情况下才存在。
一个类包含一个对赋值操作符assignation operator (=)的默认定义,该操作符用于两个同类对象之间。这个操作符将其参数对象(符号右边的对象) 的所有非静态 (non-static) 数据成员复制给其左边的对象。
2、用class obj;的方式声明一个对象,如果构造函数没有参数,或只有默认构造函数,后面不能加(),因为编译器会误以为这是一个没有参数的函数声明;
3、如果任何其它有任意参数的构造函数被定义了,默认构造函数和拷贝构造函数就都不存在了。在这种情况下,如果你想要有empty construction和copy constructor ,就必需要自己定义它们。
4、对基本类型,在c++里面,为了模板template,规定他们可以使用类似于类的默认构造函数的方式(仅仅是类似的方式而已) 赋初始值0。这叫做基本类型的显示初始化, 请参考 C++标准程序库(The C++ Standard Library)的14页,2.2.2 基本型别的显示初始化,书中举的例子就是
int i1;//未初始化
int i2 = int(); //初始化为0
sizeof一个空类等于多少?
sizeof一个空类返回1。所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。C++编译器不允许对象为零长度。试想一个长度为0的对象在内存中怎么存放?怎么获取它的地址?为了避免这种情况,C++强制给这种类插入一个缺省成员,长度为1。如果有自定义的变量,变量将取代这个缺省成员。
十三、继承或多重继承情况下构造函数的调用顺序
(1)如果声明为Derive: public Super1, public Super2{AnotherClass m_obj;}; 构造函数的调用顺序是:Super1, Super2, AnotherClass, Derive.
(2)如果父类有默认构造函数,或没有参数的构造函数,不需要在子类的构造函数定义中显式调用父类构造函数,否则需要调用。成员对象也是一样道理。以上面的例子说明,Derive的构造函数写法是:
Derive(int i): Super1(i), Super2(i), m_obj(1){...}
注意,成员对象初始化时应该指明对象名称,而不是类名。
析构函数的调用顺序应该是依次反过来的。
十四、虚函数、纯虚函数和抽象类、虚析构函数
虚函数的作用和运行原理
(1)多态是面向对象编程中的核心概念,就是说一个基类类型的指针实际上可能指向的是一个子类对象。只有在运行时才能根据实际情况来决定执行哪个函数,也就是动态联编。和动态联编对应的是静态联编,也就是说在编译时就决定了调用哪个函数。为了实现动态联编,必须将父类的函数声明为virtual。如果没有声明为virtual,可能得到的结果不是预期中的。
对于析构函数而言,虚函数保证子类和父类的析构函数都会被执行。
参考:
http://jiamingjun03.blog.163.com/blog/static/11687677620099297435263/
(2)对于包含了至少一个虚函数的类(或其父类包含虚函数),编译器需要为这个类增加4个字节,用来保存指向虚函数表VTABLE的指针。
纯虚函数和抽象类
包含了纯虚函数的类不能被直接实例化,可以称为抽象类。定义方法:
virtual void func()=0;
子类override一个虚函数,不一定要加virtual关键字。
什么情况下需要指定析构函数为virtual?
(1)析构函数不一定需要定义为虚函数,只有当这个类要作为其他类的父类使用时,才需要定义为虚函数。如果父类的析构函数没有定义为虚函数,则子类对象销毁时,父类析构函数不会被调用。
(2)对于一个抽象类,析构函数可以被定义为纯虚的。
(3)父类和子类之间的虚函数动态联编不会因为private发生影响。
(4)一个类的虚函数在它自己的构造函数和析构函数中被调用的时候,它们就变成普通函数了,不“虚”了。也就是说不能在构造函数和析构函数中让自己“多态”。
(5)在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。
十五、多重继承情况下如何引用父类的同名成员?
重继承情况下,如果多个基类有同名成员,引用方法是:
pDeriveObj->BaseClass1::Member;
十六、运算符重载
语法:
Type Type::operator +(const Type &i){}
十七、友元
友元可以实现外部对private和protected成员的访问。有两种实现:
(1)友元函数。语法:在函数声明前加上friend。友元函数并不是类的成员函数,实现函数体或调用函数时不加ClassName::。
(2)友元类。在类的声明中加入:friend class VisitorClass;
友元不会被子类继承。
十八、模板
函数模板
实现语法:
在函数的声明和实现前加上template<typename T> ,(typename和class等价),可以写在一行里面,也可以分成两行写,注意>后面没有分号。如果声明和实现分开写,两个地方都要写上template<typename T>。(1)一行代码中同时声明并实现函数
template <typename T>void func(T t1, T t2)
{...};
(2)分两行代码声明并实现函数
template <class T>
void func(T t1, T t2)
{...};
(3)在两个地方分别声明和实现函数
template <class T>
void func(T t1, T t2);
...
template <class T>
void func(T t1, T t2)
{...}
引用语法:
func<int>(5, 6);
类模板
声明方法:
类模板可以实现在一个类中有一个通用类型的成员变量。
template <class T> class ClassName
{public:
T *m_pVariable;
};
引用方法:
ClassName<int> obj;
模板特殊化
模板特殊化可以专门为某种数据类型定义特殊的行为。类的定义必须和通用的模板类完全一致,除了用专门语法,并将T修改为专门的类型,并定义特殊行为。
template<> class<int>{定义通用函数,定义特殊函数};
定义模板的默认值
template <class T = char> // 有一个默认值。
模板的参数值
除了模板参数前面跟关键字class 或 typename 表示一个通用类型外,函数模板和类模板还可以包含其它不是代表一个类型的参数,例如代表一个常数,这些通常是基本数据类型的。
二十、类型转换和C++高级类型转换
基本类型强转有两种写法:
int i;
float f = 3.14;
i = (int) f;
i = int ( f );
高级类型转换
ANSI-C++ 标准定义了4种新的类型转换操作符: reinterpret_cast, static_cast, dynamic_cast 和const_cast。
reinterpret_cast可以将一个指针转换为任意其它类型的指针。
ClassA* pa;
ClassB* pb=reinterpret_cast<ClassB*>pa;
static_cast可以执行所有能够隐含执行的类型转换,以及它们的反向操作(即使这种方向操作是不允许隐含执行的)。用于类的指针,也就是说,它允许将一个引申类的指针转换为其基类类型(这是可以被隐含执行的有效转换),同时也允许进行相反的转换:将一个基类转换为一个引申类类型。不会检查被转换的基类是否真正完全是目标类型的。
Derive* pa;
Super* pb;
pa = static_cast<Derive*> pb;
pb = static_cast<Super*> pa;
static_cast除了能够对类指针进行操作,还可以被用来进行类中明确定义的转换,以及对基本类型的标准转换:
double d=3.14159265;
int i = static_cast<int>(d);
dynamic_cast 完全被用来进行指针的操作。它可以用来进行任何可以隐含进行的转换操作以及它们被用于多态类情况下的方向操作。然而与static_cast不同的是, dynamic_cast 会检查后一种情况的操作是否合法,也就是说它会检查类型转换操作是否会返回一个被要求类型的有效的完整的对象。
在不合法的情况下,如果用于指针,将返回NULL;如果用于引用,抛出异常。
Derive* pa = new Derive();
Super* pb = new Super();
pa = dynamic_cast<Derive*> pb; //失败,返回NULL
pb = dynamic_cast<Super*> pa; //成功
const_cast类型转换对常量const 进行设置或取消操作。
class C {};
const C * a = new C;
C * b = const_cast<C*> (a);
typeid (object_pointer)
这个操作符返回一个类型为type_info的常量对象指针,这种类型定义在标准头函数中。type_info::name()返回对象的类名。
二十一、命名空间
定义一个命名空间:
namespace ns1{...}
设置默认命名空间:
using namespace ns1;
引用其他命名空间的类型:
ns2::variable = xx;
二十二、预处理命令
#undef 完成与 #define相反的工作,它取消对传入的参数的宏定义
#ifdef, #ifndef, #if, #endif, #else and #elif
指令#line 可以使我们对这两点进行控制,也就是说当出错时显示文件中的行数以及我们希望显示的文件名。它的格式是:
#line number "filename"
下面这段代码将会产生一个错误,显示为在文件"assigning variable", line 1 。
#line 1 "assigning variable"
int a?;
这个指令将中断编译过程并返回一个参数中定义的出错信息
#error
这个指令是用来对编译器进行配置的,针对你所使用的平台和编译器而有所不同。
#pragma
二十三、预定义宏
__LINE__ 整数值,表示当前正在编译的行在源文件中的行数。
__FILE__ 字符串,表示被编译的源文件的文件名。
__DATE__ 一个格式为 "Mmm dd yyyy" 的字符串,存储编译开始的日期。
__TIME__ 一个格式为 "hh:mm:ss" 的字符串,存储编译开始的时间。
__cplusplus 整数值,所有C++编译器都定义了这个常量为某个值。如果这个编译器是完全遵守C++标准的,它的值应该等于或大于199711L,具体值取决于它遵守的是哪个版本的标准。
JSON让你一看就懂
最新推荐文章于 2023-05-09 15:35:08 发布