101 - C/C++

  1. C++ 有两类创建对象的方式: 1). 分配在栈上 2). 分配在堆上

     class Foo: public Bar {
         public:
             Foo() { std::cout << "Empty constructor" << std::endl; }
             Foo(const Foo&) { std::cout << "Copy constructor" << std::endl; }
             Foo(const char* str) { std::cout << "char constructor: " << str << std::endl; }
             ~Foo() { std::cout << "destructor" << std::endl; }
     };
     // 对象分配在在栈上, 当离开该作用域时, 会自动 delete 该对象
     Foo foo; // 调用 Empty constructor
     Foo foo {}; // 同上
     Foo foo = {} // 同上
    
     Foo foo("test"); // 调用带参数的构造方法
     Foo foo {"test"}; // 同上
     Foo foo = {"test"}; //同上
    
     Foo foo(); // 这是个方法声明, 不是创建对象!
    
     // 理论上是先调用了 Empty constructor 然后再调用了 Copy constructor
     // 不过一般编译器都会进行优化, 所以实际上等价于 Foo foo;
     Foo foo = Foo();
     Foo foo = Foo {}; // 同上
    
     Bar bar = Foo {} // 这里编译器不会优化, bar 的实际类型是 Bar 而不是 Foo!
    
    
     // 对象分配在堆上, 需要自己 delete 该对象
     Foo *foo = new Foo();
     Foo *foo = new Foo {}; // 同上
     delete foo;
    

    new/delete 操作的都是 pointer.

    Prefer {} initialization over alternatives unless you have a strong reason not to.

    Calling constructors in c++ without new

    What is difference between instantiating an fooect using new vs. without

    C++ default Constructor not being called

    Why is list initialization (using curly braces) better than the alternatives?

  2. 初始化私有的 static 成员变量:

     // foo.hpp
     class Foo {
         private:
             static int bar;
     };
    
     // foo.cpp
     Foo::bar = 1;
    

    一开始觉得 foo.cpp 访问不到 bar, 觉得应该通过方法初始化, 不过转念一想, 应该和方法一样 define 是不受访问控制影响的.

    static member variable when declared private

  3. 友元声明在 private, protectedpublic 中没有任何区别.

    声明为友元的类或方法可以访问该类的私有成员.

    Friend declaration in C++ - difference between public and private

  4. virtual 相当于 Java 指令里的 invokevirtual.

    不管一个方法是否被声明成 virtual, 它都可以被子类 override.

    当调用一个不是 virtual 的方法时, 调用哪个方法是在编译时就确定的. 相反, virtual 的方法是在运行时决定调用哪个的, 即多态.

     class Foo {
         public:
             void print() { std::cout << "Foo print" << std::endl; }
             virtual void virtualPrint() { std::cout << "Foo virtual print" << std::endl; }
     };
    
     class Bar: public Foo {
         public:
             void print() { std::cout << "Bar print" << std::endl; }
             virtual void virtualPrint() { std::cout << "Bar virtual print" << std::endl; }
     };
    
     Foo *foo = new Bar {};
     foo->print(); // Foo print
     foo->virtualPrint(); // Bar virtual print
    

    C++ Virtual/Pure Virtual Explained

  5. C++ 中没有 super 的关键字, 通过 namespace 调用父类的方法. 主要还是因为 C++ 支持多继承, 用 super 的话会有 MRO 的问题.

    How to call a parent class function from derived class function?

  6. 继承时指定的 private, protectedpublic 就是对继承关系的访问控制.

    Difference between private, public, and protected inheritance

  7. A reference can be thought of as a constant pointer (not to be confused with a pointer to a constant value!) with automatic indirection, i.e. the compiler will apply the * operator for you.

    但是你还是得把它当成一个对象来用.

     Foo& foo = *(new Foo {});
    

    What are the differences between a pointer variable and a reference variable in C++?

make

  1. Makefile 由 rules 组成, 语法如下:

     target ... : prerequisite ... 
     <tab>recipe
     <tab>...
    

    或者

     target ... : prerequisite ... ; recipe
     <tab>recipe
     <tab>...
    

    Rule Syntax

  2. make 默认执行第一个名称开头不是 . 的 target.

    How does “make” app know default target to build if no target is specified?

  3. 同一个 target 可以出现在多个 rule 中, 只要多条 rule 中的 prerequisite 有一个修改时间晚于 target 那么 recipe 就会被执行. 如果有多个 recipe, 那么只有最后出现的那条 rule 的 recipe 会被执行, 同时会抛出 warning.

    但是, 上面说的情况并不是总是成立的:

    target 后面如果接的是 :: 而不是 : 那么这条 rule 被称为 double-colon rule. 如果一个 target 出现在多个 rule 中时这些 rule 必须是相同的类型.

    如果同一个 target 出现在多个 rule 中, 并且这些 rule 是 double-colon rule 时, 这些 rule 是相互独立的, 只要一条 rule 中的 prerequisite 修改时间晚于 target 那么 recipe 就会被执行.

    Multiple Rules for One Target

    Double-Colon Rules

  4. 通常变量赋值使用 =, 这样赋值的变量被称为 recursively expanded variables:

     foo = $(bar)
     bar = $(ugh)
     ugh = Huh?
    
     # 输出 Huh?
     all:;echo $(foo)
    

    GNU make 变量赋值还可以使用 := 或者 ::= (::= 是 POSIX 标准), 这样定义的变量被称为 simply expanded variables:

     x := foo
     y := $(x) bar
     x := later
    

    等价于

     y := foo bar
     x := later
    

    The Two Flavors of Variables

  5. 默认情况下 (不带 -s 选项) make 执行时, 会输出 recipe 执行的命令. 但是如果 recipe 加上 @ 前缀, 那么命令本身就不会被输出, 对于 echo 这种命令很有用.

    Recipe Echoing