What & How & Why

C++ 初探

第 1 章笔记


HelloWorld

函数

  • 函数:一段可以被反复调用的代码,包含:
    • 返回类型(void 为不返回)
    • 函数名:用于调用
    • 形参列表:表示函数接收的参数类型,可以为空,为 void,可以无形参
    • 函数体:具体的执行逻辑

// int 是返回类型
// main 后的括号是形参列表(为空)
int main()
{
    std::cout << "Hello World!" << std::endl;
    return 0;
}
// pInfo 是 parameter
// test1, test2 是 argument
void  fun(const char* pInfo)
{
     std::cout << pInfo << std::endl;
}

// 无形参的写法
// 通常是执行逻辑中不包含变量,但为了接口需要保留该变量的形参位置
// 因此只写形参类型

void  fun2(const char* pInfo, int)
{
     std::cout << pInfo << std::endl;
}

int main(int argc, char const *argv[])
{
   fun("test1");
   fun2("test2", 1);
   return 0;
}

Main函数

  • main 是整个程序的入口,是操作系统调用 C++ 的接口
  • 返回值为 int, 0 为正常返回。没有 return 默认 main 返回 0

linux 下可以通过下面的命令检查程序的返回值(是否成功运行):

#check the return type of the program
$echo $?

main 的形参列表

int main(int argc, char* argv[]) {}

其他内容

  • 类型:C++ 引入的,用于赋予某段连续内存空间意义
  • 语句:表明执行的操作
  • 注释:单行,多行

系统 IO

</code cpp> 定义于 iostream 中 如果使用双引号,系统会在当前目录中找 如果使用三角括号,系统会优先找库中的内容 自定义 header 通常给 .h 后缀 #include <iostream> #include “myheader.h” </code>

Iostream

  • 输入流:cin
  • 输出流:cout / cerr / clog
    • cerr 可以定向到不同的文件中,作为错误的输出

#cout 的内容会输出到 OutputText
#cerr 的内容会输出到 ErrorText
./main > OutputText 2>ErrorText

  • clogcerr 的区别在于是否立即刷新缓冲区
    • cerr 输出的是错误信息,因此需要立即刷新缓冲区
    • clog 输出的是日志信息,因此不会立即刷新缓冲区
  • 手动刷新:std::flush / std::endl,但大量刷新存在性能问题,因此推荐只在必要的时候使用。

缓冲区是内存中的一个区域,用于优化读写的速度。缓冲区满了以后,会一次性将内容输出。但问题在于,程序没有正常结束时,缓冲区中的内容会丢失。如果为了查找程序的 Bug,那么这部分内容是需要保留的。这种情况下,需要立即刷新缓冲区。

命名空间

命名空间的存在是为了防止命名冲突。多人开发的过程中很可能会存在同名函数,放在一起可能会存在调用的冲突。这种情况下可以选择将其分置于不同的命名空间内:

namespace NS1 {
    void fun() {
        std::cout << "NS1" << std::endl;
    }
}

namespace NS2 {
    void fun() {
        std::cout << "NS2" << std::endl;
    }

}
没有标明命名空间的函数属于全局空间global space):
void fun()
{
    std::cout << "global fun";
}
调用的方式有三种:

  • :: 域解析符调用:

// call
int main(int argc, char const *argv[])
{
    NS1::fun();
    NS2::fun();
}

  • using 调用

using namespace NS1;
fun();
# output
# 如果 NS1 中定义了 fun()
NS1
# 如果 NS1 中没有定义 fun(), 全局中定义了 fun()
global fun

  • 如果都定义了 fun(),课程中说会先调用全局 fun(),但测试发现 gcc 13.1.0 报错二义性。慎用
  • using 会导致整个命名空间暴露给其他使用者,应当尽量使用域解析符。
  • 名字空间别名:使用别名代替完整的命名空间名进行调用:

namespace n = NS1;
n::fun();

std 命名空间
  • std 命名空间是 C++ 标准库的命名空间,用法与上述的命名空间用法相同。
  • name mangling:C++ 会为不同命名空间下的相同 name 进行变化,为链接使用:

# checking the mangling data
nm main.o - o
# demangling,查看 mangling 之前的原始数据
nm main.o - o | c++filt -t

main() 不会做 mangling,因为 main() 是唯一的。

控制流

  • if statement:通过选择分支来选择执行的代码
    • 条件部分:判断是否执行
      • 使用相等==)而不是赋值 =
      • 赋值表达式会被解释为 (y = 42 → 42),而 42 会被转化为布尔值 true,因此赋值表达式恒为真。
      • 使用 const 阻止赋值表达式作为调价
    • 语句部分:要执行的操作
  • while statement:循环条件判断

结构体 / 自定义类型

  • 结构体:可以将不同的数据放置在一起,并使用 . 操作符访问内部元素
  • 结构体可以作为参数被传入函数
  • 结构体可以设置自己的成员函数

struct Point
{
    int x = 1;
    int y = 2;
    //member function
    Point fun(Point p)
    {
        p.x += 1;
        p.y += 1;
        return p;
    }
};

int main(int argc, char const *argv[])
{
    Point p;
    //call
    p = p.fun(p);
    std::cout << p.x << " " << p.y << std::endl;
}