cxxFAQ

六月 16, 2022 [c++, FAQ] #c++ #FAQ

cxxFAQ

FAQ

Why can I access private variables in the copy constructor?

The access modifiers work on class level, and not on object level. That is, two objects of the same class can access each others private data.

// https://stackoverflow.com/questions/4117002/why-can-i-access-private-variables-in-the-copy-constructor
Field::test_copy(const Field& f)
{
  pFirst = new T[f.capacity()];

  pLast = pFirst + (f.pLast - f.pFirst);
  pEnd  = pFirst + (f.pEnd - f.pFirst);
  std::copy(f.pFirst, f.pLast, pFirst);
}

vector<base > b & vector<derive> d

这两个类是没有继承关系的; vector<base*> &bref = d; bref[0]->virtualFun(); // 是未定义行为

static inline staticMember = 1

POD类型

https://en.cppreference.com/w/cpp/language/classes#Trivial_class POD 这个概念在 C++20 deprecated 了,代之以 trivial type 等概念。

#include <iostream>
#include <type_traits>
 
struct A
{
    int m;
};
 
struct B
{
    B() {}
};
 
int main() 
{
    std::cout << std::boolalpha;
    std::cout << std::is_trivial<A>::value << '\n';
    std::cout << std::is_trivial<B>::value << '\n';
}

读写锁

int read() {
    shared_lock<shared_mutex> sl(mutex_);
    return val_;
  }

void write(int x) {
    unique_lock<shared_mutex> ul(mutex_);
    val_ = x;
  }

为什么C++的this指针会出现偏移?

多继承中的static_cast

#include <iostream>
using std::cout;

struct A {
    A() = default;
    virtual ~A() = default;
};
struct B {
    B() = default;
    virtual ~B() = default;
};
struct C: A,B {
    C() = default;
    virtual ~C() = default;
};

int main() {
  C obj;
  C *pc = &obj;
  B *pb = static_cast<B*>(pc);
  A *pa = static_cast<A*>(pc);
  std::cout << "pc:" << pc << "\tpb:" << pb << "\tpa:" << pa << "\n";
}

一般pa==pc(A 是C的第一继承类) pb会偏移8字节(虚函数指针, 如果A中有其他成员变量,便宜加上变量的size)

C++弱类型不定精度数值计算的简易实现

#include <iostream>
#include <string>
#include <type_traits>
#include <variant>

using number_t = std::variant<bool, char, signed char, unsigned char, wchar_t, char8_t, char16_t, char32_t, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, float, double, long double>;

number_t operator+(const number_t &lhs, const number_t &rhs)
{
	return std::visit<number_t>(std::plus<>(), lhs, rhs);
}

std::string to_string(const number_t& x)
{
	return std::visit([](auto x) { return std::to_string(x); }, x);
}

int main() {
	number_t a = true;
	number_t b = '0';
	number_t c = a + b;
	// bool + char = int
	std::cout << c.index() << std::endl;
	std::cout << to_string(c) << std::endl;
}

进程占用cpu很高,通过perf top查看大部分占用在_raw_spin_lock是怎么回事?

perf top -g 先看下调用栈,也就是看下如何引发的spinlock perf record -g perf report -g

-fvisibility=hidde 选项在编译动态库的时候,确实可以隐藏符号;编译静态库的时候似乎做不到

大内存初始除了memset还有没有别的更高效的方案?

如果只是动态分配内存,大块0初始化内存用 calloc() 会比 malloc() + memset() 更快,因为可以利用上系统的 zero page,如果写入概率很低,实际内存分配的开销会被分摊到后续写入时。 不过你这里是静态分配的,肯定本身就比动态分配快了。关键是为啥要每次都要清理这么大吧,能否计数实际使用的字节数,只清理用了的部分

C++是否可以选择性定位符号?

两个同名方法不在同一个.a(或者再简化一下,可以认为有源码,可以任意组合编译,但是不允许修改源码),但是需要链接至同一个服务进程(Server),server的listen端接受上游请求,使用JSON版本serialize方法,接受请求后转发至下游,使用PB版本serialize方法。 可以想到一种方式链接成两个进程,listen进程链接JSON版本库,收请求后以IPC的方式交给另一个进程,另一个进程链接PB版本库,负责发往下游。

如果是动态库运行时链接的话,可以用 dlsym,https://linux.die.net/man/3/dlsym

#include <dlfcn.h>
int main() {
  /* open the needed object */
  void* handle1 = dlopen("./libfoo1.so", RTLD_LOCAL | RTLD_LAZY);
  void* handle2 = dlopen("./libfoo2.so", RTLD_LOCAL | RTLD_LAZY);

  /* find the address of function and data objects */
  auto fptr1 = (void (*)())dlsym(handle1, "my_function");
  auto fptr2 = (void (*)())dlsym(handle2, "my_function");

  /* invoke function, passing value of integer as a parameter */
  (*fptr1)();
  (*fptr2)();
}

静态可以试试objcopy符号改名, 然后dlsym

具体操作:

ar x xxx.a得到一堆.o objcopy对对应的.o改符号名 ar cu创建新的.a

string2int

// CTAD
std::vector v = {1,2,34};
std::string int2str(const int &i) {
  std::stringstream ss;
  std::string s;
  ss << i;
  ss >> s;
  return s;
}
int str2int(const std::string &s) {
  std::stringstream ss;
  int i;
  ss << s;
  ss >> i;
  return i;
}

C++11 中 std::piecewise_construct 的使用

map 类型的 emplace 处理比较特殊,因为和其他的容器不同,map 的 emplace 方法把它接收到的所有的参数都一起转发给 pair 的构造函数。但是对于一个 pair 来说,它既需要构造它的 key 又需要构造它的 value。如果我们按照之前普通的语法使用变参模板的话,则它是无法区分哪些参数用来构造 key, 哪些用来构造 value的。

这个时候就需要使用 C++11 为我们提供的一个特殊类型 piecewise_construct_t 来帮助它们找到各自正确的构造函数了。

std::piecewise_construct_t 是一个空类,全局变量 std::piecewise_construct 就是该类型的一个变量。

因为 emplace 会把参数完美转发给 pair 进行构造,所以保证一致性,你需要使用同样的语法来完成 emplace 的调用, 使用 forward_as_tuple 替代 make_tuple,该函数会帮你构造一个 tuple 并转发给 pair。 具体代码如下所示

std::map<std::string, std::complex<double>> scp;
scp.emplace(std::piecewise_construct, ///< 此常量值作为构造 pair 对象的第一个参数传递,以选择构造函数形式,通过将两个元组对象的元素转发给它们各自的构造函数来构造其成员。
    std::forward_as_tuple("hello"), ///< 该函数会帮你构造一个 tuple 并转发给 pair 构造,并存储在 first 字段
    std::forward_as_tuple(1, 2)); ///< 该函数会帮你构造一个 tuple 并转发给 pair 构造,存储在 second 字段

std::cout << scp["hello"] << std::endl;

optinal variant any 原位构造

https://blog.csdn.net/yihuajack/article/details/117781831

std::optional<std::vector<int>> opt{std::in_place, 4, 1};
std::optional<std::complex<double>> opt2{std::in_place, 0, 1};
auto opt = std::make_optional<std::vector<int>>(4, 1);
// 归功于从 C++17 开始的强制拷贝优化,不会有临时对象参与进来

std::variant<int, float> intFloat { std::in_place_index<0>, 10.5 };
std::variant<int, float> intFloat { std::in_place_type<int>, 10.5 };

std::any a{std::in_place_type<UserName>,"hello"};
auto a = std::make_any<UserName>{"hello"};
m.emplace(std::piecewise_construct,
    std::forward_as_tuple("c"),
    std::forward_as_tuple(10, 'c'));

remore erase

std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {
               return blacklist.find(i) != blacklist.end();
             }),
             digits.end());

copy_backward fill_n copy_n transform