各种阴间坑
- make的时候报错找不到链接库
- 查找类型的时候,可能和类内的名称冲突导致编译失败
- 纯虚函数可以被实现,并且可以被子类调用
- malloc(0)、realloc(0)
- 有符号右移是UB
- 很阴间,右值指针不能++,--,包括前置和后置
- 数组指针初始化
- 多继承的时候调用特定函数
- c++虚函数重载的时候
- linux内存映射,bus error
- 关于const&和性能
- 关于对齐
- 关于静态变量初始化,造成死循环
- 0
- cout << func
- 模板函数指针
- 虚函数 + 默认参数
- shared_ptr
- lambda捕获成员变量的问题
- ostream打开失败
- st表
- LoadLibrary加载不出来
- devc++下静态调用dll
- c++数值最值
- 宏可以转发参数
- memcpy和memmove的区别
- c++与动态链接库
- 内存对齐
- std::conditional不是惰性求值
- c++ 默默的编写了哪些函数
- 异常
- 成员变量应该为private
- C++对象中数据成员的初始化顺序为其在类中声明的顺序,而不是成员初始化列表中的顺序
- const成员函数可以修改static成员
- 构造函数可能抛出异常
- 析构函数不应该抛出异常
- 可变参数宏
- c++存储空间
- const& 等效于右值
- 顶层const 与底层const
- free,delete什么时候会导致程序崩溃
- C++中的new operator、operator new与placement new
- 可变参数宏
- 链接与全局变量
- 标准库元函数conditional
- UB摧毁一切定义
- O2优化下,UB很容易爆出
- 模板类中静态成员引发的全局变量构造顺序问题
- define + 模板
make的时候报错找不到链接库
一个取巧的办法是
LD_LIBRARY_PATH环境变量加上缺失的动态库路径
正常的办法是使用cmake链上
查找类型的时候,可能和类内的名称冲突导致编译失败
struct test {
int map;
void f(map<int,int> a)
}
这里的map会首先查找到int map导致编译失败
纯虚函数可以被实现,并且可以被子类调用
C++primer上面有。抽象类可以在cpp里面实现纯虚函数,子类可以调用这个函数,但是该抽象类依然不能被实例化
malloc(0)、realloc(0)
malloc(0)返回并不是null
realloc(0)返回是0
有符号右移是UB
...裂开.jpg...
很阴间,右值指针不能++,--,包括前置和后置
很阴间,出现在华子的考试题里面
数组指针初始化
using type = int (*)[10];
int tmp[10][10];
type a = tmp[0]; ok
int tmp[10];
type a = &tmp; ok
多继承的时候调用特定函数
/*
From XDU'mzb
*/
#include <bits/stdc++.h>
using namespace std;
class A {
public:
void func() {
cout << "A" << endl;
}
};
class B {
private:
bool func() const {
cout << "B" << endl;
}
};
class C : public A,public B {
};
int main () {
C test;
test.A::func();
return 0;
}
c++虚函数重载的时候
除了协变的规则,注意const属性必须保持一致
否者虚函数会形成重载
linux内存映射,bus error
发生错误的原因是因为mmap不能去扩展一个内容为空的新文件,因为大小为0,所有本没有与之对应的合法的物理页,不能扩展。
用write往里面写个字符就行
完整场景
关于const&和性能
const&不一定能提高性能,因为解引用指针以及其他的因素会导致性能下降
关于对齐
对齐的原则是任何大小为K的对象的起始位置都是K的倍数
关于静态变量初始化,造成死循环
在对malloc函数进行动态库打桩的时候发现的
void* malloc(size_t size){
static auto ___ = init(); // 其中init中有malloc操作
}
这里会引起一个死循环,因为___没有完成初始化,在此重入malloc的时候,依旧会进入init函数,形成死循环
这个好像是叫recursive_init_error
/*
---- From XDU's mzb
*/
//#include <boost/any.hpp>
//using namespace boost;
#include <bits/stdc++.h>
using namespace std;
using ll = long long int;
int init(int (*test)()){
return test();
}
int test(){
static int ___ = init(test);
return 0;
}
int main()
{
test();
return 0;
}
0
0可以自动转为指针类型
在函数重载中,可能匹配到空函数指针参数
cout << func
输出一个bool值
模板函数指针
auto func = min
低版本的编译器会罢工 unable to deduce 'auto' from 'min
然后类带模板指针的时候,一定要严格对应模板函数的类型,否则很容易匹配错误
虚函数 + 默认参数
可以运行,按指针的类型寻找对应的函数填参数
/*
---- From XDU's mzb
*/
#include <bits/stdc++.h>
#include <boost/any.hpp>
using namespace std;
using ll = long long int;
struct base
{
virtual void show(string rhs = "base")
{
cout << "base = " << rhs << "\n";
}
virtual ~base(){}
};
struct sub : base
{
virtual void show(string rhs = "sub")
{
cout << "sub = " << rhs << "\n";
}
virtual ~sub(){}
};
int main()
{
base * a = new sub();
sub * b = new sub();
a -> show();
b -> show();
return 0;
}
shared_ptr
f(shared_ptr<string>(new string()),
shared_ptr<string>(new string()));
如果第二个new抛出异常,第一个new出来的内存不会被释放,正确的做法是使用**make_shared**
lambda捕获成员变量的问题
会捕获this,然后相当于引用捕获成员变量...没有一点报错或者警告
/*
---- From XDU's mzb
*/
#include <bits/stdc++.h>
#include <boost/any.hpp>
using namespace std;
using namespace boost;
using ll = long long int;
struct my
{
ll data;
my()
:data(0)
{}
void f()
{
auto lambda =
[=]()
{
// 这里捕获了this
data++;
// this -> data++;
};
lambda();
cout << "data = " << data << endl;
}
};
int main()
{
my().f();
return 0;
}
ostream打开失败
在dll里面,ofstream.open需要使用绝对路径,相对路径会失败,很奇怪,错误码 5
可以用GetLastError得到最后一次的错误码
st表
st表的跳转可能需要把整个表刷出来
默认的st表是不需要全部刷出来的,但是在某些很特殊的情况下,是需要全部刷出来的
看情况
LoadLibrary加载不出来
const char dll_name[] = "E:\\InjectDll\\Inject_dll_test.dll";
需要双斜杠,路径单斜杠会GG,GetLAstError返回126(无法找到指定模块)
devc++下静态调用dll
不支持#pragam comment(lib,"dll")
需要从菜单 -> 项目属性 -> 链接 -> 添加库或者对象导入
好像导入.dll或者.a(也就是.lib文件)中的一个就行了
一个都不导入会出问题
c++数值最值
#include <limits>
cout << numeric_limits<int>::max() << endl;
宏可以转发参数
学到了.jpg
#include <bits/stdc++.h>
using namespace std;
using ll = long long int;
void f(ll n)
{
cout << "f" << n << endl;
}
void g(ll n)
{
cout << "g" << n << endl;
}
#define f(x,y,z) g(x)
int main()
{
f(10,12,23);
return 0;
}
memcpy和memmove的区别
源和目标不重叠的时候两个函数没区别
源和目标重叠的时候,memmove保证结果正确
memmove用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
c++与动态链接库
c++函数导出的时候,会重整函数名,也就是函数名会变的非常阴间
用c方式导出好很多
dll导出对象的时候,有很复杂的问题
内存对齐
跨编译器/平台交互的时候一定要注意内存对齐的情况
c++ #pragma pack(x)
windows默认最小对齐x为4字节
std::conditional不是惰性求值
可以通过加壳的方式实现一个惰性求值的conditional
template<template<typename...> class op,typename ...Args> // 惰性求值
struct late_op
{
using type = typename op<Args...>::result;
};
然后调用std::conditional<0,xxx,xxx>::type::type即可,Stack Overflow中提供的思路,很巧妙
很多难题都可以通过增加一个间接层来实现
const
const 与 指针
int * p; //指向变量,指针可赋值
const int * p; //指向常量,指针可赋值
int const * p; //指向常量,指针可赋值
const int * const p; // 指向常量,指针不可赋值
const 和 构造函数
非 explicit 可能触发构造函数
#include <bits/stdc++.h>
using namespace std;
using ll = long long int;
struct node
{
ll val;
node(ll val)
:val(val)
{}
};
void f(node const& rhs)
{
cout << rhs.val << "\n";
}
int main()
{
f(5);
return 0;
}
const 与 类
1.const可以触发const成员函数重载
必要的时候需要const_cast来调用重载函数
2.const成员函数可以修改非const的static成员
3.可以用mutable来修饰需要修改的常量对象中的成员
4.当const成员函数和非const成员函数差不多
可以用const_cast简化代码
c++ 默默的编写了哪些函数
1.默认构造函数
2.默认析构函数
3.默认拷贝构造函数
4.默认赋值运算符
5.取地址运算符
6.const的取地址运算符
取地址这个知识比较阴间...我也是偶然知晓
struct Empty
{
Empty();
~Empty();
Empty(const Empty&);
Empty& operate = (const Empty&);
Empty* operate & ();
const Empty* operate&() const;
};
异常
1.析构函数不能也不应该抛出异常
如果释放资源可能抛出异常,写个普通函数处理
2.绝不要再构造,析构的时候调用虚函数
构造析构的时候,调用虚函数等效于调用父类的虚函数
因为此时类型是基类类型
包括typeid显示的是基类类型
3.成员swap不应该抛出异常
全局swap可能抛出异常
成员变量应该为private
proctected并不比public更具封装性
该用final用final
C++对象中数据成员的初始化顺序为其在类中声明的顺序,而不是成员初始化列表中的顺序
尽量保持生命顺序和初始化顺序一致
const成员函数可以修改static成员
构造函数可能抛出异常
这个处理比较复杂,后面补
析构函数不应该抛出异常
标准库的析构函数都不抛出异常
可变参数宏
... + __VA_ARGS__比较阴间,以后全部采用下面这种形式
/*
---- Author : XDU_mzb
---- lisp 2.0
*/
#include <bits/stdc++.h>
using namespace std;
using ll = long long int;
ll f(ll a,ll b,ll c)
{
return a + b + c;
}
#define add1(args...) f(1,2,##args)
#define add2(args...) f(args)
int main()
{
cout << add1(3) << endl;
cout << add2(1,2,3) << endl;
return 0;
}
c++存储空间
堆、栈、自由存储区、全局/静态存储区、常量存储区
自由存储区就是new出来的空间
堆就是malloca出来的空间
很多情况下,operator new的全局实现就是malloca
但是,可以通过存在operator new,改变new的实现
所以自由存储区和堆是两个概念,标准中未说明
const& 等效于右值
可能触发构造函数
#include <bits/stdc++.h>
using namespace std;
using ll = long long int;
struct test
{
test(ll val)
{
cout << "con" << endl;
}
~test()
{
cout << "des" << endl;
}
};
void f(test const& a) {}
int main()
{
f(2);
return 0;
}
顶层const 与底层const
char s[] = "1231221";
const char * a = s;
char * const b = s;
const char * const c = s;
以 * 为界
const char *a,a = xxx ok,a[0] = xx 错
char * const b,b = xxx 错,b[0] = xx ok
free,delete什么时候会导致程序崩溃
接上一条,delet的底层实现是free
只有free(一个没有malloca)的地址的时候,程序会卡住/崩溃
这个情况发生在new数组的时候,有效地址会 -= 4,也就是头部加了一个记录长度的内存
C++中的new operator、operator new与placement new
https://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html
https://www.cnblogs.com/likaiming/p/9393083.html
都在这了
可变参数宏
#define link(a,b) a##b
int main()
{
ll ca = 0;
cout << cp(c,a) << endl;
中,##代表字符串连接
但是,在可变参数宏中
#define FOO(...) f(10,##__VA_ARGS__)
int main()
{
cout << FOO() << endl;
这两个##的用处是当可变参数宏为空时,避免f(10,)
也就是消除多余的逗号
具体见这个博客 http://blog.sina.com.cn/s/blog_af95b18b01018o9y.html
链接与全局变量
强符号 : 函数名,或者已经初始化的全局变量
弱符号 : 未初始化的全局变量
多强报错
一强多弱选强
多弱的时候...任选一个,这就非常的阴间,会导致非常阴间的错误
标准库元函数conditional
conditional不是惰性求值...
什么是惰性求值呢,简单来说就是
ll *p = new ll(10);
std::cout << (p == 0 ? 0 : *p) << "\n";
p == 0的时候就不会执行*p
在c++的位运算也是惰性求值的
但是,conditional不是惰性求值的,当你写出类似上述代码的时候
编译器会不断的做实参演绎...有时候conditional的第二个参数是死循环...编译器就会编译到死机...
UB摧毁一切定义
运算中的各种阴间溢出导致的UB
特别是有符号整形的溢出是UB...一些运算在开O2优化的时候会直接导出非常诡异的错误
O2优化下,UB很容易爆出
不开O2你的代码可能在很正常的运行
但是...开O2之后,一切都变了
比如你原本的代码会数组越界,开O2有时会直接死循环...这种错误还比较难查
模板类中静态成员引发的全局变量构造顺序问题
...挺阴间的
#include <bits/stdc++.h>
using namespace std;
using ll = long long int;
template<typename T>
struct wapper
{
static set<T> val;
using type = T;
T value;
wapper()
{
val.insert(value = mex());
}
operator T ()
{
return value;
}
ll mex()
{
for (T i = 0;;i++)
if (val.count(i) == 0)
return i;
return -1;
}
};
template<typename T>
set<T> wapper<T>::val = set<T>();
wapper<ll> a;
int main()
{
return 0;
}
wapper
解决方案1 :大力偏特化
template<>
set<ll> wapper<ll>::val = set<ll>();
wapper<ll> a;
解决方案2 :大佬的做法(不太懂...慢慢理解
塞静态函数里面
作为静态函数的静态变量存在
以避开顺序问题
多线程 有可能的话 可以使用 thread_local
搜了一下...全局变量初始化这块有一些阴间东西...特别是不能依赖多个头文件的全局变量的初始化顺序...
再加上特殊的模板静态变量...qs非常阴间
define + 模板
#define CREATE_NAME(type,type_name) template<> struct typeinfo< type > \
{ \
static std::string name() \
{ \
return type_name; \
} \
}
不知道为啥不能写成
#define CREATE_NAME(type,type_name) \
template<> struct typeinfo< type > \
还有一个很阴间的
#define CREATE_NAME(type,type_name) template<> struct typeinfo< type > \
{ \
static std::string name() \
{ \
return type_name; \
} \
}
using ll = long long int;
CREATE_NAME(ll,"ll");
CREATE_NAME(long long int,"ll");
CREATE_NAME(ll,"ll");这个是无效的...不知道为啥...必须写成CREATE_NAME(long long int,"ll");...测试的时候没测出来...引发了一堆错才查到...
特别是CREATE_NAME(ll,"ll");这可以通过编译...没有任何报错...阴间东西
本文来自博客园,作者:XDU18清欢,转载请注明原文链接:https://www.cnblogs.com/XDU-mzb/p/14815190.html