考虑这个简单的程序
#include <iostream>
struct A
{
int x1234;
short x56;
char x7;
};
struct B : A
{
char x8;
};
int main()
{
std::cout << sizeof(A) << ' ' << sizeof(B) << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这打印8 12.即使B可以在不破坏对齐要求的情况下打包成8个字节,但它却占用了贪婪的12个字节.
这样会很好sizeof(B) == 8,但答案是结构的大小是否必须
是该结构对齐的精确倍数?表明没有办法.
因此,当以下时我感到惊讶
struct MakePackable
{
};
struct A : MakePackable
{
int x1234;
short x56;
char x7;
};
struct B : A
{
char x8;
};
Run Code Online (Sandbox Code Playgroud)
印刷8 8 …
restrictC++中缺少C中的关键字,所以出于兴趣,我一直在寻找一种在C++中模拟相同功能的方法.
具体来说,我希望以下内容相同:
// C
void func(S *restrict a, S *restrict b)
// C++
void func(noalias<S, 1> a, noalias<S, 2> b)
Run Code Online (Sandbox Code Playgroud)
哪里 noalias<T, n>
T*用->和访问时一样*T*(这样函数可以被称为func(t1, t2),where t1和t2are都是类型T*)n指定变量的"混叠类",使类型的变量noalias<T, n>和noalias<T, m>可从未假定为别名对于n!=米.这是我的严重缺陷解决方案:
template <typename T, int n>
class noalias
{
struct T2 : T {};
T *t;
public:
noalias(T *t_) : t(t_) {}
T2 *operator->() const {return static_cast<T2*>(t);} …Run Code Online (Sandbox Code Playgroud) 我有一个大图(100k节点),其中每个节点必须存储每个传出边的一些信息.std::vector<bool>我没有将其保留在一个中,而是使用dynamic_bitsetBoost 1.58来执行按位操作.每个节点还保持指向某个多态对象的指针.一个最小的例子看起来像这样,
struct Node
{
std::vector<size_t> succ;
boost::dynamic_bitset<> succ_flags;
std::unique_ptr<Object> data;
};
Run Code Online (Sandbox Code Playgroud)
考虑这个简单的基准程序,它创建并销毁图表:
#include <vector>
#include <boost/dynamic_bitset.hpp>
#include <memory>
constexpr int N = 50000;
struct Node
{
std::vector<size_t> succ;
boost::dynamic_bitset<> succ_flags;
//std::unique_ptr<int> data;
Node(int i)
{
for (int j = i; j < N; j += i) succ.emplace_back(j);
succ_flags.resize(succ.size());
}
};
int main()
{
std::vector<Node> nodes;
for (int i = 1; i <= N; i++) nodes.emplace_back(i);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在time命令下运行,典型的结果是
real 0m0.055s …Run Code Online (Sandbox Code Playgroud) 我最近需要一个通过引用捕获多个局部变量的lambda,所以我制作了一个测试片段来研究它的效率,并-O3使用clang 3.6 编译它:
void do_something_with(void*);
void test()
{
int a = 0, b = 0, c = 0;
auto func = [&] () {
a++;
b++;
c++;
};
do_something_with((void*)&func);
}
Run Code Online (Sandbox Code Playgroud)
movl $0x0,0x24(%rsp)
movl $0x0,0x20(%rsp)
movl $0x0,0x1c(%rsp)
lea 0x24(%rsp),%rax
mov %rax,(%rsp)
lea 0x20(%rsp),%rax
mov %rax,0x8(%rsp)
lea 0x1c(%rsp),%rax
mov %rax,0x10(%rsp)
lea (%rsp),%rdi
callq ...
Run Code Online (Sandbox Code Playgroud)
显然,lambda只需要一个变量的地址,所有其他变量都可以通过相对寻址来获得.
相反,编译器在堆栈上创建了一个包含指向每个局部变量的指针的结构,然后将结构的地址传递给lambda.这和我写的一样:
int a = 0, b = 0, c = 0;
struct X
{
int *pa, *pb, *pc;
};
X x = {&a, …Run Code Online (Sandbox Code Playgroud) 想象一个项目,其中有一个如下所示的接口类:
struct Interface
{
virtual void f()=0;
virtual void g()=0;
virtual void h()=0;
};
Run Code Online (Sandbox Code Playgroud)
假设其他地方,有人希望创建一个类实现这个接口,为此f,g,h都做同样的事情.
struct S : Interface
{
virtual void f() {}
virtual void g() {f();}
virtual void h() {f();}
};
Run Code Online (Sandbox Code Playgroud)
然后,它会生成一个虚函数表的有效优化S其条目都指向S::f,从而节省的包装函数的调用g和h.
但是,打印vtable的内容表明不执行此优化:
S s;
void **vtable = *(void***)(&s); /* I'm sorry. */
for (int i = 0; i < 3; i++)
std::cout << vtable[i] << '\n';
Run Code Online (Sandbox Code Playgroud)
0x400940
0x400950
0x400970
与clang和gcc之间的切换一样编译-O3或-Os …
从不使用
new
delete
release
并且更喜欢使用
std::make_unique
std::unique_ptr
std::move
reset (冗余)
应该在道德上导致没有内存泄漏:新的指针只能在智能指针内部创建,因为我们不允许使用它们release.
因此,人们可能会试图使用这种编码风格,然后再也不用费心去检查内存泄漏 - 无论在哪里抛出异常,智能指针的RAII语义都应该在堆栈解开时清理任何悬空指针.
除了C++之外,还有很多令人讨厌的惊喜.根据我的假设反复粉碎的经验,我不禁想到可能会有一些角落案件无论如何都会导致内存泄漏.更糟糕的是,可能有一种明显的方式来释放指针的所有权而不是release自身.或其它智能指针类没有一个explicit构造函数可能会意外地摄取通过获得的原始指针get,导致双重的FreeS ...
有漏洞吗?如果有,可以通过添加一些更简单的限制来修复它们吗?(不分配任何内存不算数!)如果可以达到一组防止所有类型的内存错误的编码指南,那么完全忘记内存管理的细节是否可以?
c++ ×6
c++14 ×2
boost ×1
c++11 ×1
coding-style ×1
inheritance ×1
lambda ×1
memory-leaks ×1
padding ×1
restrict ×1
vector ×1
vtable ×1