g++
等于 gcc -xc++ -lstdc++ -shared-libgcc
如果使用g++链接对象文件,它将自动链接到STD C++库中
#define __GXX_WEAK__ 1
#define __cplusplus 1
#define __DEPRECATED 1
#define __GNUG__ 4
#define __EXCEPTIONS 1
#define __private_extern__ extern
例如:
namespace xxx
{
}// 没有分号
std::cout << "xxx";
using namespace std;
不建议使用using namespace
,相当于把垃圾分类后,又导入同一个垃圾车,依然会冲突。
同名的namespace自动合并
无名名字空间
无名名字空间中的成员使用 ::标识符
进行访问
支持嵌套
n1::n2::num;
namespace n1
{
int num = 1;
namespace n2
{
int num = 2;
namespace n3
{
}
}
}
别名
namespace n123 = n1::n2::n3;
当函数被声明为内联函数之后, 编译器会将其内联展开(类似宏展开), 而不是按通常的函数调用机制进行调用.
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。只有当函数只有 10 行甚至更少时才将其定义为内联函数。
注意:inline的使用是有所限制的,inline只适合涵数体内代码简单的涵数使用,不建议包含复杂的结构控制语句,例如while、switch,并且不能内联函数本身不能是直接递归函数。
以下情况不宜使用内联:
类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构
函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。
inline函数仅仅是一个对编译器的建议,所以最后能否正常使用内联,看编译器的意思,编译器如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联编译时就接受内联。声明内联函数只是建议而已!
typedef
,在定义结构变量时,可以省略 struct
关键字.
或 ->
,但是 C 的结构成员可以是函数指针class和struct的区别?
typedef
,在定义结构变量时,可以省略 union
关键字.
或 ->
,但是 C 的结构成员可以是函数指针C 语言中 void*
可以与任意类型指针自动转换。
C++ 中 void*
不能给其他类型的指针直接赋值,必须强制类型转换,但其他类型的指针可以自动给 void *
赋值。
C++ 为什么这样修改 void*
?
为了更安全,所以 C++ 类型检查更严格。
const常量,inline函数,static函数都可以在头文件中定义。
普通函数
static函数
可以在任何文件定义,但在cpp中定义的静态函数对于其他文件不可见,而在头文件中定义的静态函数在包含该头文件的文件中可见(static函数具有文件作用域)。
内联函数
最好只在头文件中定义。因为同一原形(实现可能不同)的内联函数可以在不同的cpp中重复定义,但是编译器会把这些原形看作一个内联函数,因此运行时会出现不确定现象。放在头文件中可以避免这种情况。而且放在cpp中的内联方法对于其他文件是不可见的。
类一般只在头文件中定义,在cpp中实现其成员方法的定义。
类中的成员包括:普通成员方法, static成员方法,普通成员变量,static成员变量,const成员变量,static const成员变量等。
普通成员方法
inline
)static成员方法
ps: 在类外部定义的时候要去掉static关键字,因为类里面的static表示该成员属于类,而文件中的static表示文件作用域,这是完全两回事。
普通成员变量
类内部声明和定义
只能在构造函数的初始化列表中初始化
C++98
=
在声明时初始化。=
进行初始化()
对自定义类型进行初始化{}
对元素集合统一初始化C++11可以使用 =
或者 {}
就地初始化,类似于Java语言
struct init{
int a = 1;
double b{1.2};
};
需要注意的是=和{}可以和初始化列表一起使用,而且初始化列表总是后作用于=和{}
用户可以不进行初始化(编译器将默认构造)
const常量
const int *ptr = new int[5]
),最好在cpp中定义,因为动态空间只有运行时才能确定,编译器并不能用常量值直接代替ptr;且头文件在多处被引用后可能带来内存泄露、异常行为等。全局变量
静态全局变量
类对象的构造顺序是这样的:
C++里面是不能定义常量数组的!因为3和5的矛盾。
int?j?=?15;
class?Bclass{
int?f?=?100;
float?g?=?200.0;
const?float?h?=?30.0;
const?int?a=10;
//?const?int?array[20]; // 编译报错
//?int?thesecondarray[20]?=?{?0?}; // 编译报错
int?&b=j;
int?&k?=?f;
static?int?c;
static?const?int?d=30;
static?const?float?e;
public:
Bclass() = default;
};
int?Bclass::c?=?20;
const?float?Bclass::e?=?40.0;
基本类型都可以在类内初始化。
static const int
能在类内初始化,其他的static还是要在类外初始化虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。
虚基类构造函数的参数必须由最新派生出来的类负责初始化(即使不是直接继承):
什么是虚函数表,在C++的类中,一旦成员函数中有虚函数,这个类中就会多一个虚函数表指针,这个指针指向一个虚函数表,表里面记录了这个类中所有的虚函数,当这个类被继承,它的子类中也会有一个虚函数表(不管子类中有没有虚函数),如果子类的成员函数中有函数签名与父类的虚函数一样,就会用子类中的函数替换它在虚函数表中的位置,这样就达到了覆盖的效果。
当通过类指针或引用调用函数时,会根据对象中实际的虚函数表记录来调用函数,这样就达到了多态的效果。
多态类中的虚函数表建立在编译阶段。
已验证:每个类的实例时共享虚函数表的(参考:《C++单个类的所有对象是否共享虚函数表的验证》),这有点类似于类成员变量。
decltype与auto关键字一样,用于进行编译时类型推导,不过它与auto还是有一些区别的。decltype的类型推导并不是像auto一样是从变量声明的初始化表达式获得变量的类型,而是总是以一个普通表达式作为参数,返回该表达式的类型,而且decltype并不会对表达式进行求值。
int i = 4;
decltype(i) a; //推导结果为int。a的类型为int。
与using/typedef合用,用于定义类型。
using size_t = decltype(sizeof(0));//sizeof(a)的返回值为size_t类型
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);
并不推荐以下方式,因为 auto
关键字更合适:
vector<int> vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++) {
//...
}
泛型编程中结合auto,用于追踪函数的返回值类型(这也是decltype最大的用途了)。
template <typename _Tx, typename _Ty>
auto multiply(_Tx x, _Ty y)->decltype(_Tx*_Ty) {
return x*y;
}
decltype推导四规则
T&&
T&
。T
。宽字符是“中文”、“日文”等除ascii之外各种字符编码的统称。即用来表示需要多个字节来表示的数据类型。
Unicode
是宽字符编码的一种,已经被现代计算机指定为默认的编码方式。
所以,Windows系统中的宽字符,就是指的Unicode,虽然本质上Unicode只是宽字符的子集而已。
Unicode本质是字符集,在这个集合中的任意一个字符都可以用一个四字节来表示。
UTF-8是编码规则,由于Unicode字符集的大部分常用字无需4字节表示(前2字节都是0),编码规则就是用不定长字节(可能2byte/3byte/4byte)表示一个变长的编码。
“你好”对应的 Unicode
分别为"U+4f60"和"U+597d”,对应的 UTF-8
编码分别为“0xe4 0xbd 0xa0”和“0xe5 0xa5 0xbd”。
多字节字符串在编译后的可执行文件以UTF-8编码保存:
#include <stdio.h>
#include <string.h>
int main(void) {
char s[] = "你好";
size_t len = strlen(s);
printf("len = %d\n", (int)len); // 输出:len = 6
printf("%s\n", s);
return 0;
}
对编译后的可执行文件执行 od
命令,,可以发现"你好"以UFT-8编码保存,也就是“0xe4 0xbd 0xa0”和“0xe5 0xa5 0xbd”6个字节。
宽字符串在编译后可执行文件中以Unicode保存:
#include <wchar.h>
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "zh_CN.UTF-8"); // 设置locale
wchar_t s[] = L"你好";
size_t len = wcslen(s);
printf("len = %d\n", (int)len); // 输出:len = 2
printf("%ls\n", s);
return 0;
}
对编译后的可执行文件执行 od
命令,发现宽字符串是按Unicode保存在可执行文件中的。
目前宽字符在内存中以Unicode进行保存,但是要write到终端仍然需要以多字节编码输出,这样终端驱动程序才能识别,所以printf在内部把宽字符串转换成多字节字符串,然后write出去。这个转换过程受locale影响,setlocale(LC_ALL, "zh_CN.UTF-8");设置当前进程的LC_ALL为zh_CN.UTF-8,所以printf将Unicode转成多字节的UTF-8编码,然后write到终端设备。如果将setlocale(LC_ALL, "zh_CN.UTF-8");改为setlocale(LC_ALL, en_US.iso88591):打印结果中将不会输出"你好"。
一般来说程序在内存计算时通常以宽字符编码,存盘或者网络发送则用多字节编码。
c语言中提供了多字节字符串和宽字符串相互转换的函数。
#include <stdlib.h>
size_t mbstowcs(wchar_t *dest, const char *src, size_t n); // 将多字节字符串转换为宽字符串
size_t wcstombs(char *dest, const wchar_t *src, size_t n); // 将宽字符串转换为多字节字符串
Demo如下:
#include <locale.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <wchar.h>
#include <string.h>
wchar_t* str2wstr(const char const* s) {
const size_t buffer_size = strlen(s) + 1;
wchar_t* dst_wstr = (wchar_t *)malloc(buffer_size * sizeof (wchar_t));
wmemset(dst_wstr, 0, buffer_size);
mbstowcs(dst_wstr, s, buffer_size);
return dst_wstr;
}
void printBytes(const unsigned char const* s, int len) {
for (int i = 0; i < len; i++) {
printf("0x%02x ", *(s + i));
}
printf("\n");
}
int main () {
char s[10] = "你好"; //内存中对应0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00
wchar_t ws[10] = L"你好"; //内存中对应0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00
printf("Locale is: %s\n", setlocale(LC_ALL, "zh_CN.UTF-8")); //Locale is: zh_CN.UTF-8
printBytes(s, 7); //0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00
printBytes((char *)ws, 12); //0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00
printBytes((char *)str2wstr(s), 12); //0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00
return(0);
}
编译后,执行结果如下:
Locale is: zh_CN.UTF-8
0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00
0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00
0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00
第二行输出也印证了我们之前说的多字节字符串在内存中以UTF-8存储,"0xe4 0xbd 0xa0 0xe5 0xa5 0xbd"正是"你好"的UTF-8编码。
第三行输出印证了之前说的宽字符串在内存中以Unicode存储,"0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00"正好是宽字符串L"你好"对应的Unicode。
setlocale(LC_ALL, "zh_CN.UTF-8")
设置locale,程序将以UTF-8解码宽字符串。调用 mbstowcs()
后,可以看到“你好”的 UTF-8
编码 "0xe4 0xbd 0xa0 0xe5 0xa5 0xbd 0x00"确实被转换成了“你好”对应的 Unicode
: "0x60 0x4f 0x00 0x00 0x7d 0x59 0x00 0x00 0x00 0x00 0x00 0x00"。
如果将 setlocale(LC_ALL, "zh_CN.UTF-8")
换成 setlocale(LC_ALL, "en_US.iso88591 ");
那么最后一行的输出也就会不一样。
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*.................................
* do something here
*.................................
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* MONGOOSE_HEADER_INCLUDED */
按照C语言的方式声明函数。
典型的,一个C++程序包含其它语言编写的部分代码。类似的,C++编写的代码片段可能被使用在其它语言编写的代码中。不同语言编写的代码互相调用是困难的,甚至是同一种编写的代码但不同的编译器编译的代码。例如,不同语言和同种语言的不同实现可能会在注册变量保持参数和参数在栈上的布局,这个方面不一样。
为了使它们遵守统一规则,可以使用extern指定一个编译和连接规约。例如,声明C和C++标准库函数strcyp(),并指定它应该根据C的编译和连接规约来链接:
extern "C" char* strcpy(char*,const char*);
注意它与下面的声明的不同之处:
extern char* strcpy(char*,const char*);
下面的这个声明仅表示在连接的时候调用strcpy()。
注意:extern "C"指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。
extern "C"指令仅指定编译和连接规约,但不影响语义。例如在函数声明中,指定了extern "C",仍然要遵守C++的类型检测、参数转换规则。
cppHeader.h
#ifndef CPP_HEADER
#define CPP_HEADER
extern "C" void print(int i);
#endif CPP_HEADER
cppHeader.cpp
#include "cppHeader.h"
#include <iostream>
using namespace std;
void print(int i)
{
cout<<"cppHeader "<<i<<endl;
}
在C的代码文件c.c中调用print函数:
// extern void print(int i); // 声明调用函数
#include "cppHeader.h"
int main(int argc,char** argv)
{
print(3);
return 0;
}
注意在C的代码文件中直接#include "cppHeader.h"头文件,编译出错。
原文:https://www.cnblogs.com/brt2/p/12982781.html