枫叶小爱的世界

过好这简单的一生

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

配置hexo

安装node.js

从官网下载:https://nodejs.org/en/
git bash验证下,如下结果,说明安装成功了。
  avatar

安装hexo

npm install -g hexo-cli
git bash验证下,如下结果,说明安装成功了。
  avatar

Blog初始化

hexo init / hexo init blog_dir_name

hexo init fengye_blog

如上会在目录下生成 fengye_blog 目录;‘

hexo生成和运行

hexo generate(g)

会在 fengye_blog\public\ 生成 index.html的静态网页;
  avatar

hexo server(s)

运行后可以在 http://localhost:4000/ 看到效果图

hexo 更新主题

blog目录下直接clone对应的主题代码:

git clone https://github.com/iissnan/hexo-theme-next themes/next

配置hexo主题

认识2个配置文件

  • Next 主题配置文件

    主要是配置 Next 主题相关的设置,不过不改仓库下的默认数据了,现在通过一个新的文件 _config.next.yml 修改就行。

  • Hexo 配置文件

    Hexo 根目录下的文件———_config.yml,用于配置Hexo基本信息的。

配置 favicon

icon分几种:

  1. 网站显示在浏览器tab中的icon;
  2. 网站内的icon;

hexo配置的问题

  • FATAL Port 4000 has been used. Try other port instead.

    答:已经开了一个hexo 服务了,不过也可以指定端口:hexo s p xxxx

  • 更新了Next主题后,网页显示不了?

    答:删除后重新更新了下Next主题代码,然后就正常了;

参考链接

  1. 简书blog: https://www.jianshu.com/p/343934573342
  2. https://baidinghub.github.io/2020/03/02/Windows%E4%B8%8B%E4%BD%BF%E7%94%A8hexo%E6%90%AD%E5%BB%BA%E5%8D%9A%E5%AE%A2/#2%EF%BC%89%E6%B7%BB%E5%8A%A0%E6%9C%AC%E5%9C%B0%E7%9A%84ssh%E5%88%B0Github%E4%B8%AD
  3. Next 主题的官方配置说明:https://theme-next.js.org/docs/getting-started/
  4. 关于 .yaml 的说明:https://theme-next.js.org/docs/getting-started/configuration.html

const 那些事

强烈推荐看:《C++编程思想-8章常量》。

1. const 含义

const=constant,
常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。
举个例子,就是 const int test_a = 12, 你是没法给 test_a 赋值的。

const 关键字指定变量的值是常量,并通知编译器防止程序员对其进行修改。

经典一问:const 变量是否可以被修改?

答1:可以。const 修饰的始终是变量,如果const 变量位于栈区,那么是可以通过取地址修改。

但是,如果const 变量位于全局,那么是不可以修改的。

1
2
3
4
5
6
7
8
9
10
// const 变量可以修改的示例
const int kGlobalValue = 312;
int main() {
const int const_value = 100;
int * ptr = (int *)&const_value; // 通过取const 变量的地址,就可以修改const变量的值。
*ptr = 200;

int* ptr_globale = (int*)&kGlobalValue;
*ptr_globale = 400; // 运行的时候会报错哦!!!
}

答2:Linux上 全局常量是放在 .rdata段的,该段只读,所以尝试修改就会报错。
为什么C++无法修改全局静态常量?(Linux平台示例-知乎)

const作用

  • 定义常量

    const int a=100;

  • 类型检查

    const常量有类型,编译器会进行安全检查;#define宏定义只是字符串替换,不进行安全检查;

    #define 也是有类型的,详细见:讨论#define和const的类型问题

  1. const 定义的变量只有类型为整数或枚举,且以常量表达式初始化时才能作为常量表达式。
  2. 其他情况下它只是一个 const 限定的变量,不要将与常量混淆。
  • 防止修改,起保护作用,增加程序健壮性

    修饰函数参数等,如果参数被修改会保存。

    1
    2
    3
    void f(const int i){
    i++; // error!
    }
  • 节省空间,避免不必要的内存分配

    const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数。

    const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。

理解常量和常量表达式

理解常量和常量表达式——掘金blog

const的存储位置探究(Todo)

  1. const修饰的量不是常量,仅仅是个只读量。在编译的时候全部替换赋值的const常量,类似#define替换;
    在运行的时候该const变量可通过内存进行修改。

    总结来说就是,const修饰的数据,编译的时候会进行替换,这样被赋值的数据运行的时候数据是编译期定好的。
    即使你修改了 const 修饰的变量,被赋值的数据不会因为运行时的修改而变化。
    但是const 能修改的前提必须是可读可写的,比如栈上的变量,全局的就不行。

  2. CSDN关于const存储位置的讨论
    CSDN总结的blog, 我觉得写的还可以

const对象的范围

默认为文件局部变量。
注意: 非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。

非const修饰的变量在不同文件的访问

1
2
3
4
5
6
7
8
9
// file1.cpp
int ext = 132; // 默认都可以访问

// file2.cpp
// 需要指定ext为extern变量,否则找不到。
extern int ext;
int main() {
int test_ext = ext;
}

const 修饰的变量在不同文件的访问
const 修饰的想外部访问,定义的时候就得加 extern;且需要初始化。

1
2
3
4
5
6
7
8
9
10
// file1.cpp
const int ext_const_def = 132; // 默认当前文件可访问
extern const int ext_const_extern = 312; // 想外部访问,需要定义的时候指定为 extern

// file2.cpp
// 需要指定ext为extern变量,否则找不到。
extern const int ext_const_extern;
int main() {
int test_ext = ext_const_extern;
}

extern const 未初始化会报链接错误:无法解析的外部符号;
avatar

const用法

const修饰普通类型的变量

1
2
3
4
5
6
7
8
const int b = 10; 
b = 0; // error: assignment of read-only variable ‘b’

// 等价于
int const b = 10;

const string s = "helloworld";
const int i,j=0 // error: uninitialized const ‘i’

作为常量,直接赋值修改是不行的哦!
作为常量,必须初始化哦!

C2734 “const_value”: 如果不是外部的,则必须初始化常量对象。

const与指针

1
2
3
4
const char * a; // 指向常量的指针,a 指针类型是 const char 的,即指向的内容不能被修改。
char const * a; // 同上
char * const a; // 指针本身是常量即 a 这个地址是常量,不能被修改, 也称常指针、const指针。
const char * const a; // 指向const对象的const指针。

常量指针: 指向的对象是常量。不能修改指针所指向的内容。

const 位于 * 的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;

1
2
3
4
5
6
7
8
9
10
    // 编译环境 msvc2017
int main() {
int const_test_a = 123;
const int* const_ptr_a; // 指向常量的指针 不初始化是OK的
const_ptr_a = &const_test_a; // 赋值给常量指针的时候,可以是 const,也可以是非const。

// 可以修改 const_test_a 的值,只是不能通过 *const_ptr_a 来修改
int* ptr_test_a = &const_test_a;
*ptr_test_a = 312; // OK的
}

常量指针的注意点:

  1. const_ptr_a 指针不用赋初值,但不能通过 const_ptr_a 修改所指向的对象的值;
  2. 不能使用void指针保存const对象的地址,必须使用const void类型的指针保存const对象的地址;
  3. 可以把非const对象的地址赋值给 const 对象的指针;但不能通过 const 定义的那个指针修改对象值。

指针常量: 指针本身是常量。不能修改指针的地址。

const 位于 * 的右侧,const就是修饰指针本身,即指针本身是常量。

指针常量的注意点

  1. const指针必须初始化,且const指针的值不能修改。(就是说const指针存储的地址不能动哦)
  2. 1
    2
    3
    4
    5
    6
    7
    8
        // 编译环境 msvc2017
    int main() {
    int num=0;
    int * const ptr=# //const指针必须初始化!且const指针的值不能修改
    int * t = # // 通过取地址得到 num 的地址,就可以修改 num的值。
    *t = 1;
    // 总结一下:*ptr,*t 其实都是保存的 num的地址,只不过 *ptr是const修饰的,不能直接改num的值。
    }

指向常量的常指针

1
2
3
4
5
    // 编译环境 msvc2017
int main() {
const int p = 3;
const int * const ptr = &p; // ptr指向的是常量,且ptr本身是一个const指针,保存的地址不能动。
}

知乎关于const指针讲的比较容易懂的blog
通俗易懂理解什么是常量指针和指针常量——知乎

const应用到函数中

  • const修饰函数返回值
      1. const int —— 本身无意义,函数返回本身就是要赋值给其他变量的。
        1
        2
        // 编译环境 msvc2017
        const int func1();
      1. const int* —— 指针指向的内容不变
        1
        2
        // 编译环境 msvc2017
        const int* func2();
      1. int* const —— 指针本身即指针的地址不能变
        1
        2
        // 编译环境 msvc2017
        int *const func2();
  • const修饰函数参数
      1. 传过来的参数以及指针本身在函数内不可变,没有意义
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        // 编译环境 msvc2017
        void func(const int var); // 传递过来的参数不可变
        void func(int *const var); // 指针本身不可变

        void TestConstPtr(int *const var) {
        int test = *var;
        }

        int main() {
        int num = 3211;
        int * const ptr_num = # //const指针必须初始化!且const指针的值不能修改
        TestConstPtr(ptr_num);
        }
        // 确认 结果:
        // &ptr_num:
        // &var:
        // 上面2个地址是不同的,所以 var 是函数参数的临时变量。

        值传递方式, 形参本身会产生临时变量,参数无需保护,所以加不加const没有意义。
        const int var 和 int *const var 都是值传递。
        理解下 int *const var 为什么没有意义?
        答1:const修饰的是指针本身,所以此处只是修饰 var 本身不变;
        答2:另外,指针传递的时候,var 指向的数据依然可能被修改,所以只限制 var 本身没有意义。

      1. 参数指针指向的内容为常量不可变
        1
        void StringCopy(char *dst, const char *src); // 限定 char* 可以保证指向的内容不被修改

        试图修改 src 时,编译会报错。

      1. 参数为引用,为了增加效率同时防止修改
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        void funcA(const A a); // 值传递,效率低,对象的构造、复制、析构都会消耗时间。

        void funcA(const A& a); // 使用引用——变量的别名,避免值传递的开销。

        void funcA(const A& a); // const限定对象的引用
        void funcA(const std::string& value); // 具体例子:std::string

        // 一个疑问: 是否所有的都用引用更好?
        // 答:没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。
        void funcA(const int& a);

        1. 引用只是参数的别名而已,不需要产生临 时对象。 2. const 限定的引用,可以避免引用被修改。

    • const 引用传递的使用场景

      • 非内部数据做输入参数:建议使用 const 引用,提高效率;
      • 内部数据类型做输入参数:不建议用,比如 int、double 等类型;

const类中的用法

  • const成员函数:只要不修改数据成员,就应该声明为const;

    const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
    const 成员函数调用非const成员函数的不行的哦!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // const成员函数调用 非const成员函数示例,编译报错
    // E1086 对象含有与成员 函数 "Apple::FuncB" 不兼容的类型限定符
    // C2662: “int Apple::FuncB(void)”: 不能将“this”指针从“const Apple”转换为“Apple &”
    /*
    解释下 C2662: “int Apple::FuncB(void)”: 不能将“this”指针从“const Apple”转换为“Apple &”

    FunA中调用FuncB的时候,要加入this。
    this->FuncB();

    const Apple* this->FuncB(); // FuncB() 本身是非const的,所以报错了。
    */
    class Apple {
    public:
    Apple(int i) : apple_number(i){
    app_id = i;
    }

    int FuncA() const {
    int rest = 123;
    FuncB(); // error, 不能将“this”指针从“const Apple”转换为“Apple &”
    return rest;
    }

    int FuncB() {
    return 123;
    }

    private:
    int people[100];
    const int apple_number;
    const int app_code = 312;
    int app_id;
    };

    理解下为什么 const 对象只能访问 const 成员函数?为什么const成员函数中调用非const成员就会报错?

    答:理解下 this 指针就能明白为什么了。 this 指针是成员函数默认参数,也是有类型的,const 成员类型就是const的。

  • const成员变量

    • 必须通过初始化列表进行初始化 or 定义的时候初始化。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      class Apple {
      public:
      Apple(int i) {
      apple_number = i; // error,编译错误,『表达式必须是可以修改的左值』。
      apple_number = 123; // 如上一样。
      }

      Apple(int i) : apple_number(i) { // 放到初始化列表是OK的
      }

      Apple(int i) : apple_number(100) { // 不使用构造参数也是OK的
      }

      int FuncA(int posx, int posy) {
      return posx + posy;
      }
      private:
      int people[100];
      const int apple_number;

      const int app_code = 312; // const int 这么初始化也是OK的。

      int appid;
      };

查看C++类的内存结构方法

  • 查看所有类:/d1 reportAllClassLayout
  • 查看单个类:/d1 reportSingleClassLayoutXXX(XXX为类名)

    /d1 参数统一放到 C/C++ → 命令行:其他选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // 一个简单的类内存布局,没有虚函数等
    /*
    * 编译的时候会输出布局
    1>class Apple size(412):
    1> +---
    1> 0 | people
    1>400 | apple_number
    1>404 | app_code
    1>408 | app_id
    1> +---
    */
    // 如上,静态成员归属于所有类,所以布局上看不到。
    class Apple {
    public:
    Apple(int i) : apple_number(i){
    app_id = i;
    }

    int FuncA() const {
    int rest = 123;
    FuncB();
    return rest;
    }

    int FuncB() {
    return 123;
    }
    private:
    int people[100];
    const int apple_number;
    const int app_code = 312;
    int app_id;

    static int s_test_a; // 类的static 类内声明,不能初始化哦!
    };
    int Apple::s_test_a = 212; // static 必须类外初始化!!!
    查看C++类的内存结构方法_例子写的很好的blog

理解this指针

先说编译器怎么做的?

  1. this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给this。

  2. this 形参是隐式的,并不出现在代码中,而是编译阶段由编译器默默地将它添加到参数列表中。且只有非静态成员函数哦!

  3. 成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息。
    所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。

  4. 示例:int FuncA(int posx, int posy); 编译时可能变成了 int FuncA(Apple* this, int posx, int posy) 【不一定是这个结果,只用于示例说明】

总结一下this是什么?

答: this是编译器加给c++类的非static成员函数的额外参数,用于管理类的成员函数和变量。作为参数也有类型限定,比如使用const。

作为参数,this指针也是有类型的。

this的类型取决于成员函数的声明情况。
const 方法时,指针类型就是 const Apple, 非const 就是 Apple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int FuncA(int posx) { 
appid = posx;
return 123;
}
int FuncB(int posx) const {
appid = posx;
FuncA();
return 123;
}

// 经过编译之后,加上 this 指针的结果(个人理解,如有错误请指正)
int FuncA(Apple*this, int posx) {
this->appid = posx;
return 123;
}
int FuncB(const Apple*this, int posx) const {
this->appid = posx;
this->FuncA(); // erro。这儿就相当于是 const 对象 调用 非const 接口
return 123;
}

C++中的this指针_geeksforgeeks
C语言中文对于this指针的讲解_最后的this是什么说的还可以

const和#define区别

  • const

    修饰的是一个真正的变量,对于变量,你当然可以做任何事情。
    因为常量值在编译时已经被替换和折叠,所以不存在性能问题。

  • #define:

    只是字符串替换,
    stackoverflow_difference-between-define-and-const

参考链接

  1. 主要是参考Github上的c++那些事:https://github.com/Light-City/CPlusPlusThings
  2. MSDN对于const的说明:https://learn.microsoft.com/zh-cn/cpp/cpp/const-cpp?view=msvc-170
  3. 极客笔记c++ const的使用:https://deepinout.com/cpp-tutorial/cpp-tutorial-const-usage.html
0%