Cpp
coding base knowledge
magic number
魔数代码中出现的没有说明的数字。魔数会给阅读代码带来不便。
c++
final
final关键字可以直接写在class的类名称后面或者写在方法的后面,表示该class或者methods不能够被继承。
class MD final { // MD 不能被继承
public:
virtual void methods() final; // 不能重写methods方法
};
explicit
explict关键字用来修饰类的构造函数,写在构造函数前面。表示该构造函数只能以显示的方式进行类型转换,不能被隐式类型转换.
override
表示对该方法进行了重写
constexpr
constexpr表示修饰在编译期就可以算出来的常量。而const表示在运行时不被修改。
template位置
template代码的位置不能在.cpp/.cc之中,Templated code implementation should never be in a .cpp
file: your compiler has to see them at the same time as it sees the code that calls them.一般都是放在.h文件中. 如果希望放在.cpp文件中要对class进行 explicit instantiation,对类模板A
不能在class中特化模板.
模板的定义一般是放在.h中,但是对该模板的特化定义要放在.cpp函数中.否则会有重定义的问题.
模板只是代码的简化和宏展开一个效果,编译器会对每个类型都增加一个实现。
不要在template中对某个特殊类型进行不同逻辑的处理,如果需要这样请特化。
case
switch case只能用于判断int类型,需要使用enum将需要case的ne容转为int. case中判断条件可以用宏
class;
class最后要加上class A{};
基类要实现析构函数,否则派生类析构的时候虚函数表中找不到基类的析构函数.基类的virtual函数都必须有body实现即要写成virtual void A() {};而不是virtual void A();并且没有实现.
RAII
RAII(resource acquisition is initialization),资源获取即初始化.对象创建获取资源,对象销毁释放资源.
deconstructor
-
只有真实存在的对象离开其作用域时才会调用析构函数,对象的引用,指向对象的指针离开其作用域时,不会调用析构函数。
-
使用new运算符创建的对象的资源,只有使用delete运算符删除指向它的指针时,才会调用它的析构函数,释放它的资源。
比如class A;在A的实现的.cpp之前的函数中先声明一个A,这样可以先使用A的初始化等,而不会导致编译错误.
抽象类,虚函数
类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 “= 0” 来指定的.
比如:virtual int fun() = 0;抽象类不能被用于实例化对象,不能用模板接口.virtual不能是template.
虚函数和纯虚函数区别: 纯虚函数表示这个函数在基类中肯定是没有实现的.单但虚函数在基类中是可以有实现的.所以纯虚函数在派生类中必须有对应的实现部分. template, constructors 都不能定义为 virtual.Templates are all about the compiler generating code at compile-time. Virtual functions are all about the run-time system figuring out which function to call at run-time.
class调用methods的三种形式
class A;
A a; // a::b();
A& a; // a.b();
A* a; // a->b();
数据类型
double = float64;
// 任何类型指针变量都是4字节
for loop
c++中在for/while中不能一直改变一个变量值.比如a在每次for循环中都改变这个是不行的,要建立一个vector,每次改变不一样的值.所以不要在for循环里声明变量.可以在for循环外部声明vector.
命名
一般用name_带下划线的表示private的属性名称,另外写public函数来获取private的值,这样可以防止更改class中一些重要的属性值.
const
const std::string* A() {}; // 以指针形式返回的函数加const,用来修饰返回值是const类型,即这个指针不能被修改.
const std::string* const_value = A(); // 而且必须返回值必须赋值给const修饰的同类型指针.
std::string A() const {} // const 在函数名称后面表示该函数是常量函数,即不能修改A对象内的任何成员,只能在A里面进行读操作,不能有写操作.
const class A; // 对A的method不能进行改写,而且如果要改写const会给输出默认加上一个&,以此不允许匹配
const void *data; // const修饰*data,表示指针不能修改
void* const data; // const修饰data,即data是const变量
void *data() const {} // 表示不能在method data里修改内容,这个methos是只读的。
.so
比如程序依赖了/usr/lib64下面的某个libA.so,那么在编译的时候可以加上-lA来找到改链接.
g++ main.cpp -o main -lA
多态
多态性指相同对象收到不同输入或不同对象收到相同输入时产生不同的实现动作。分为静态多态(重载函数)和动态多态(虚函数).
dialect
A dialect of a programming language or a data exchange language is a (relatively small) variation or extension of the language that does not change its intrinsic nature.
方言可以看做是一种对数据的定义形式.比如一个node里有name, value等信息.将node从A方言改变为B方言,可能node的名字换了,value的名字也换了,但是对应的value里面所表达的值的信息是没有发生变化的.
macro
macro substitutions happen before your code is compiled,宏展开是在代码编译之前执行的,所以不能送给宏一个变量.
# 在CMakeListst.txt里使能宏
add_definitions(-DCPU_ONLY) # 定义CPU_ONLY宏,对应c++ demo: # ifdef CPU_ONLY
头文件
#include "header.h" // 表示header.h在当前目录,可以用"../header.h"加载上一级目录文件.现在也可以表示在编译器的某一个include路径当中
#include <header.h> // 表示header.h在编译器的某一个include路径当中
// 所以在任何情况都可以用""来加载头文件,但是<>只能表示在include路径中的文件.
#include <stdlib.h> // 带扩展名.h表示c标准库
#include <iostream> //不带扩展名表示c++标准库
string to char
std::string str = "name";
const char* name_char = str.c_str();
// std::cout不能直接输出int8的数据,无法显示
// int32_t* int_data ...;
// float data = *(float*)int_data; // 不能这样,要加static cast
shared_ptr
class A {};
std::shared_ptr<A> a_class; // 相当于A* a_class.
reinterpret_cast
//不用指针之间cast
namespace
namespace {} // It's called an unnamed namespace / anonymous namespace. It's use is to make functions/objects/etc accessible only within that file
函数注释
graph->create(op_name, /*num_outputs=*/1); // 函数传常量值的时候一般不清楚具体含义可以这样/*XX*/来表示这个1是什么意思.
enum
C++ has two kinds of enum
:
enum class Color { red, green, blue }; // enum class
enum Animal { dog, cat, bird, human }; // plain enum
// enum classes - enumerator names are local to the enum and their values do not implicitly convert to other types (like another enum or int)
// Plain enums - where enumerator names are in the same scope as the enum and their values implicitly convert to integers and other types
extern
// head.h
extern int a;
extern int fun();
// extern在head.h中声明全局变量或者函数申明的关键字.但是其实现是在其他.cpp函数中.在*.cpp里只要包含head.h就可以实现对应的函数fun()或者使用变量a.在编译的时候*.cpp虽然找不到fun的声明但是也不会报错.
公有继承是的子类与基类是IS-A的关系。也就是说子类是一种基类
内存
The memory that a program uses is typically divided into a few different areas, called segments:
- The code segment (also called a text segment), where the compiled program sits in memory. The code segment is typically read-only.
- The bss segment (also called the uninitialized data segment), where zero-initialized global and static variables are stored.
- The data segment (also called the initialized data segment), where initialized global and static variables are stored.
- The heap, where dynamically allocated variables are allocated from.
- The call stack, where function parameters, local variables, and other function-related information are stored.
每个程序有一个内存池,即堆(heap)又称为自由空间(free store)
// 静态内存
// 栈内存
// 堆(heap)
//
frend
if you write friend A;
, A
must be a known type name, that is it must be declared before.
常用std接口
std::ostream;
// 拼接字符,eg:
std::ostream stream;
stream << "name";
std::cout << stream.str();
std::replace();
std::regex;
**引用**
```c++
void print(std::string str) {} // 这种直接传递,函数会自动拷贝一份输入str,一般如果函数print对输入str是只读的,即不会改变输入内容的话要传一个reference进来防止对输入进行无用的copy,从而加快运行效率。
void print(const std::string& str) {} // 传引用。
void print(std::string* str) {} // 传指针,在print内对str进行操作会改变str的值。
google style
// 单句赋值等过长,需要换行空四个字符:
const DecimalNumber product =
CalculateProduct(values, options, /*completion_callback=*/nullptr); // 空四格
// if for语句下一行需要空两个空格
if (true) {
auto a = b; // 空两格
}
单例
单例不能放到头文件中,否则会导致多线程安全。
**delete**
delete用来禁用某些函数
```c++
classs A{
A() = delete; // 无法直接只用构造函数A,比如此时其他类就不能继承class A
};
typename
template位置
template代码的位置不能在.cpp/.cc之中,Templated code implementation should never be in a .cpp
file: your compiler has to see them at the same time as it sees the code that calls them.一般都是放在.h文件中. 如果希望放在.cpp文件中要对class进行 explicit instantiation,对类模板A
不能在class中特化模板.
模板的定义一般是放在.h中,但是对该模板的特化定义要放在.cpp函数中.否则会有重定义的问题.
模板只是代码的简化和宏展开一个效果,编译器会对每个类型都增加一个实现。
不要在template中对某个特殊类型进行不同逻辑的处理,如果需要这样请特化。
optional template parameter
template <typename T>
struct FUN {
typedef int Type;
...
}
template <typename T, typename = typename FUN<T>::Type>
void fun();
an optional template parameter with no name and a default value.
参考: stack param no name function prototype
分析:
对typename = typename FUN
1 FUN
2 然后是 typename FUN
3 typename = 表示declaring template type parameters, 即需要定义一个模板变量。类模板定义的时候是可以指定一个默认值的,比如:
tymplate <typename T = int>
void fun()
即指定模板参数类型是int类型。
But as any parameters in C++, the name is optional. 即参数的名字是可以省略的,所以上面的定义也可以写成:Just like function parameters, template parameters can have it’s name omitted, and can have a default value. 当把函数的变量名字省略的时候其实是一种function prototypes. A function prototype declares a function but does not define it. 即可以先不实现某个函数,先在头文件里定义。
// *.h
tymplate <typename = int>
void fun()
// *.cc
fun<> a; // init template class fun as a, and typename type is default int.
所以综上:如果typename FUN
template <typename T, typename = typename FUN<T>::Type>
void fun();
->
template <typename T, typename = int>
void fun();
->
template <typename T, typename A = int>
void fun();
->
template <typename T, int>
void fun();
这样就可以实现一个偏特化的类。这里常用的是traits class, 即根据模板参数来获取某种类型。
特化函数不能重载
假如一个基模板
template <T>
void fun(T a);
// 对其中一个类型特化
template <>
void fun<>(int a); // 这种是不对的,在使用的时候会调用到基模板中,而不是特化的function.因为specialization don't overload.
如果对一个function需要特化的,那就写一个非模板的函数而不是特化函数。
c++ compile process
source code(.c, .cpp, .cc, .h) ->preprocessing(include header, expand macro) gen .i, *.ii -> gcc compiler gen *.s -> assembler gen *.o -> liker gen *.a/.so ->executable machine code *.exe