复习c++

一些偏门的点

  1. endl可以用\n代替
    cout<<"Hello world"<<"\n"

  2. int main()括号里加void才能表示不可传参

// 这样是正确的
int main()
{
  if (0) main(42);
}

// 这样会出错
int main(void)
{
  if (0) main(42);
}

image
默认编译产生的文件名就是cpp的名且不带后面的参数。如果是在命令行窗口编译的,可以显示出arg 1--4
g++ test_main.cpp -o a a.exe abcd bcde cdef
image

  1. 命名空间可自定义,不一定非要用std
#include <iostream>

namespace MyNamespace {
    int value = 5;

    void printValue() {
        std::cout << "Value: " << value << std::endl;
    }
}

int main() {
    MyNamespace::printValue();

    MyNamespace::value = 10;

    MyNamespace::printValue();

    return 0;
}
  1. 用宏控实现注释
#if condition
  code1
#else
  code2
#endif
  1. 枚举类型默认第一个是0
    enum color { red, green = 5, blue };
    依次是0 5 6

  2. #define没有作用域限制,typedef有

  3. const修饰谁的问题
    是从右往左结合的,如下:

#define PINT int*
typedef int* pint
int i1 = 1, i2 = 2;
 //pint自带括号,所以第一句实际上是 const (int*) p1 ,这里const修饰p1
// 第二句是宏定义展开,所以实际上是,const int (*p2) ,这里修饰*p2
const pint p1 = &i1;    //p1不可更改,p1指向的内容可以更改,相当于 int * const p;
const PINT p2 = &i2;    //p2可以更改,p2指向的内容不能更改,相当于 const int *p;或 int const *p; 意思是p2存的十六进制数的这个地址上的值,不可以通过p2更改,但是可以把p2赋值给另一个指针p3,通过*p3更改,也可以直接操作地址更改,反正就是不能通过p2更改!!

** //也就是说,int *p事实上,是p先和*表示是指针类型,然后再和int结合,而不是把int*作为一个类型**
 
pint s1, s2;    //s1和s2都是int型指针
PINT s3, s4;    //相当于int * s3,s4;只有一个是指针。
  1. int long longlong字节问题
    image

  2. 自动转换的规则

  • 先转换成同一类型再运算
  • 转换方向是精度增高、字节变长、有符号转为无符号
  • float实际运算时也是double运算的
  • char和short实际运算时是int运算的
  • 赋值运算,可能会让右值精度丢失
  1. 一般情况是作用域小的优先覆盖,有例外
int g = 99;
int func();

int main()
{
    int g = 10;
    //cout << g;
    int kk = func();
    cout << kk;   //kk = 99
    return 0;
}
int func()
{
    return g;
}

改变全局变量的值

// 全局变量声明
int g = 20;
int fun1(int a,int b){
    g=a+b;
    cout<<"被改变的全局变量为:"<<g<<endl;
    return 0;
}

int fun2(){
    cout<<"此时的全局变量为:"<<g<<endl;
    return 0;
}

int main(){
    fun2();
    fun1(10,20);
    fun2();
    return 0;
}

全局变量a和局部变量a,要想在局部的作用域调用全局的a,可以用 ::a

静态局部变量生命周期是整个程序运行期间,准确的说只要加静态都是整个程序运行期间,静态局部还可以保证每次调用的都是上次的值,只会初始化第一次

  • 整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

想输出宽字符,需要先setlocale(LC_ALL, "chs") 输出用wprintf 编写代码要用utf-8!!!!

  • 字符常量是括在单引号中。如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L'x'),此时它必须存储在 wchar_t 类型的变量中。否则,它就是一个窄字符常量(例如 'x'),此时它可以存储在 char 类型的简单变量中。
  1. /不会输出任何,仅用于隔行写太长的字符串

  2. #undef可以取消宏定义,但是const取消不了

  3. 无符号化为有符号的位数运算,采取 N-2^n 的计算方法,例如 N=50000,short 为 16 位,计算方法为 50000-2^16 得到 -15536。

  4. ^按位异或 &按位与 |按位或 <<左移(右边补0) >>右移(正数左补0 负数左补1)

  5. 对于b = 2^n ,a % b == a & (b - 1),这是因为b的二进制表示为10000,除了最高位1其余全0,b-1是011111,那么a&(b-1)相当于取末几位,a / b 是相当于右移几位,所以a % b也相当于取末几位 【%8 = &7】

  6. 一种算法,abs(a-n) + abs(b-n) == n表示在边界 输出菱形

  7. swap函数不用temp

	a=a+b;
    b=a-b;
    a=a-b;
	//或者
	a=a^b;
    b=a^b; // b = (a^b)^b = a^(b^b) = a^0 = a,这个a就是原来的a
    a=a^b; //a = (a^b)^b 【此时后面的b就是a】=a^b^a = 0^b = b
  1. 字符串默认是const string 不可以用string的库函数、加法等
string s("hello");
cout<<"hello"<<endl;         //OK
cout<<s.size()<<endl;        //OK
cout<<"hello".size()<<endl;  //ERROR
cout<<s+"world"<<endl;       //OK
cout<<"hello"+"world"<<endl; //ERROR
  1. sizeof和size() length()都不一样,后两者不包括结束符\0,sizeof包含,例如"abcd" 返回 5 4 4
    char str[20] = "12345" strlen = 5 sizeof() = 20
    char *ss = "abcdefg" sizeof(ss) = 4 //指指针 sizeof(*ss) = 1//指第一个字符

注意 sizeof()填数组名,不会看作指针,而是返回数组实际的占用空间!!

  1. 任何改变vector长度的操作都会使已存在的迭代器失效,比如pushback()

  2. cout << R"(原始\t字\n符串)" << endl; 可以关闭转义
    此外还有:


L : 类型 wchar_t     	 编码 : Unicode
u : 类型: char16_t   	 编码 : UTF-16  	(C++11)
U : 类型: char32_t     编码 : UTF-32 	(C++11)
u8 : 类型 char         编码 : UTF-8     (C++11)
  1. 对于指针和数组的cout一点:
    image

  2. 小端存储,低地址位于低位,(一般情况下一个数组第一位最低,依次提高),比如0x12 0x34 0x56 0x78数组,以int打印出来(正好八位)就是0x78563412

  3. C++ 初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序,比如类内定义是x y用构造函数初始化时,y = 1; x = y就不会初始化x = 1,因为会先执行x = y !!

  4. 一个类内可以有多个构造函数,可以是一般类型的,也可以是带参数的,相当于重载构造函数,但是析构函数只能有一个。

  5. 子类在继承父类的成员变量时,会单独开辟一块内存保存父类的成员变量,因此子类自己的成员变量即使和父类的成员变量重名,也不会引起冲突,想在子类中访问父类需要用::

  6. string的str.find('x')方法,表示在str串中查找某个字符,返回值不会是0!!!!就算找不到也是-1!!!!所以不可以用于判断!!!!!

  7. to_string位于string库,作用是int转string!!!!!

  8. c++必须指定列!!!

  9. 下列结果是abc1,原因是二维数组实际上也是线性存储,而且printf、strlen遇\0停止,sizeof遇\0无影响

    char a[2][3] = {{'a', 'b', 'c'}, {'1', '\0', '2'}};
    printf("%s", a[0]);
  • sizeof("a\0") = 3!!!!!!
  1. 对于一个char ch[3] &ch++是不合理的,只可以+1,自增不合理

  2. Student *stu = nullptr这样实例化,也可以正常调用成员函数,但是不可以用this!!!

  3. ++i=1正确 i++=1错误!

基础知识

c++是静态语言,所谓静态语言就是编译时检查语法,而非运行时在检查

面向对象

  1. 封装
    对外隐藏实现细节,只公开接口。提高安全性、可靠性和灵活性。
  2. 继承
    从已有类中派生出新类,新类具有已有类的属性和方法,并且可以扩展或修改这些属性和方法。提高代码的复用性和可扩展性。
  3. 多态
    多态是指同一种操作作用于不同的对象,可以有不同的解释和实现。它通过接口或继承实现,提高代码的灵活性和可读性
  4. 抽象
    从实例中提取共同的特征,形成抽象类或接口,以封装为基础!!而不必关注底层的实现细节。

ANSI标准

为了保证c++可以运行在windows\linux\macos上,现在的编译器都符合这个标准

三字符组

之前的时候键盘有的字符打不出来或者冲突,就用??=代替# ??)代替]等,现在直接可以在键盘上找到或者转义字符

编程风格

有c fortran smalltalk等,分别代表底层,内存硬件的、科学计算的、面向对象的

数据类型

基本的就int float double char void bool wchar_t(宽字符型)
wchar_t ch2 = L'你';

修饰符有 long short signed unsigned

枚举类型

enum 枚举名{ 
     标识符[=整型常数], 
     标识符[=整型常数], 
... 
    标识符[=整型常数]
} 枚举变量;

类型转换

预留...暂时没看懂

extern关键字

用于声明一个变量或函数,让编译器在其他文件中找定义,只能定义一次但可以声明无数次
定义包含了声明,但是声明可以不定义

作用域

包含局部、全局、块、类作用域

  • 分别是函数调用创建,执行完销毁、程序开始创建,结束销毁、执行到{}创建,出括号销毁、与类相同生命周期

局部作用域是块作用域的一种特例,即函数内部的局部作用域是函数的块作用域。
块作用域可以包含更广泛的范围,例如在if语句、循环语句、switch语句等中声明的变量都属于块作用域

几个常用类型修饰符

  1. const 常量,初始化必须在定义时,后面无法更改
  2. volatile 该变量的值可能会被程序以外的因素改变,如硬件或其他线程。
  3. restrict 被restrict修饰的指针是唯一一种访问它所指向的对象的方式。C99标准加入。
    说人话就是,即使定义了其他指针等于被restrict修饰的指针,它也无法访问到restrict修饰的指针指向的对象。
  4. mutable 表示类中的成员变量可以在 const 成员函数中被修改。
class MyClass {
public:
    int getNumber() const {
        // 可以修改被mutable修饰的变量
        m_number = 10;
        return m_number;
    }

private:
    mutable int m_number;
};
  1. register 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率

存储类

用于指定变量或函数的生命周期、作用域和存储位置的关键字。

  1. auto:默认的存储类。它使变量具有自动的生命周期,定义它们的块作用域结束时,它们会自动销毁。

  2. register:定义存储在寄存器中而不是内存中的局部变量。具有更快的访问速度,但无法使用地址运算符(&)获取其地址。

  3. static:声明具有静态生命周期的变量。静态变量在程序的整个执行期间都存在,只初始化一次。静态局部变量在函数的多次调用之间保持其值不变。

  4. extern:声明在其他文件中定义的全局变量或函数。使变量或函数能够在多个文件之间共享。

  5. mutable:可变存储类是用于修饰类的成员变量,即使在const函数中也可以修改它们的值。

从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。

静态变量

  • 静态局部,作用域不变,但是每次用完它不会销毁
  • 静态全局,与全局的区别就在于,只能在文件内部,其他文件不能访问
  • 静态类,那么创建两个类对象,更新一个对象的值另一个对象也会相应更新
  • 静态类,要在类外初始化,如果是public的类,可以不创建对象直接用类名访问,比如Person::name
  • 静态成员无法使用this,因为它不属于任何对象,不需创建对象就可以调用它

extern

  • 名义上全局变量是所有文件都可用,但实际上编译是一个文件一个文件编译的,所以用其他文件的全局变量、函数需要先extern声明

thread_local

  • thread_local 声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。
  • 可以与 static 或 extern 一起用
  • 仅应用于数据声明和定义,不能用于函数声明或定义。

数字

float

  • 4字节,有效7位(因为有正负),但是默认显示6位有效数字,比如123.45678 默认显示123.456 可以保证123.4567不出错的保存

double

  • 8字节,有效16位,但是默认显示6位有效数字

cout方法

#include <iomanip>
cout << "double d :" <<setprecision(16)<<fixed<< d << endl;

  • set表示保留16位有效数字,fixed表示不使用科学计数,合起来表示保留小数点后16位
  • 默认输出double float都是六位有效数字,且四舍五入不算0
  • setprecision保留超过数字本身,但不超过精度部分补0,超过精度部分不一定,但是显示的数四舍五入后是原来的数
//设置保留8位小数点setprecision(8)<<fixed,与是否超出精度、是否超出数字本身有效位有关
float f1 = 123.4; 数字本身未超出精度
float f2 = 123.4567; 数字本身恰好是最大精度
float f3 = 123.45678; 数字本身超过最大精度
//最终结果分别为 123.40000153     123.45670319    123.45677948

总结:不论是数字本身还是保留位,都受精度影响,保留位是用来显示的,所以他不超精度部分就是四舍五入且补0 超了部分随机

数组

  1. 最低地址存第一个数,最高地址存最后一个数,依次存储
  2. 多维数组int arr[5][10][4];
  3. 数组作为形参可以是int * 、int param[10] 、 int param[]

字符串

  1. 实际上是以'\0'结尾的字符数组,而且不需要手动添加,自动添加的
  2. cstring头文件可以用strcpy strcat strlen strchr strstr等,
  3. 实际上还是用<string多,赋值可以直接等号,链接用加号,长度用.size()

IO

  1. char ch = cin.get() 或 cin.get(ch),可用于吃回车或者不用的符号
  2. char a[20]; cin.get(a,20) 输入后只接收19个字符,因为第20个要装\0
  3. string s; getline(cin, s); 获取一行
  4. cin.getline(arr,5,'a'); 读到arr里,读5个,遇'a'停止读取,不填默认以回车\n停止读取

指针

  1. NULL其实就是0,规定内存为0的地方不可访问
  2. c++11标准后推荐nullptr
  3. int *p = 0; 可以 int a = 0; int *p = a不可以
  4. 释放指针 delete p 或者 delete[] p

引用

  1. 引用初始化后不能指向其他的了
  2. 引用必须创建时初始化,而且不存在空引用
  3. int a = 1; int& b = a; 这里的b不会重新分配内存空间
  4. 指针类型的引用 int *&q = p;
  5. 右值引用??

结构体

  1. class 中默认的成员访问权限是 private 的,而 struct 中则是 public 的。
  2. 从 class 继承默认是 private 继承,而从 struct 继承默认是 public 继承。
  3. class 可以定义模板,而 struct 不可以。

面向对象

  1. 一般写法
class Box
{
   public:
      double length;
      double height;
};
  1. inline

作用就是避免函数调用,用于一些比较简短的函数,加inline关键字,表示运行到这个函数直接把定义处的代码复制过来,而不是调用过去,用程序空间换性能

类内定义的成员函数自动inline,如果只是类内声明,类外定义函数体,需要手动添加inline

inline只能写在函数定义前,不能写在声明前

  1. protected(受保护)成员在派生类(即子类)中是可访问的,派生也就是继承

  2. 继承分为public private protected三种,写法如下:

class B : public A{
  }
  1. 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

构造函数的名称与类的名称完全相同,不返回任何类型,也不返回void,可带参。构造函数可用于为某些成员变量设置初始值。
析构函数类似于构造函数,前面加个~,也没有任何返回值,而且不带参

继承

  1. 公有继承(public):父类的公有成员也是子类的公有成员,父类的保护成员也是子类的保护成员,父类的私有成员不能直接被子类访问,但是可以通过调用父类的公有和保护成员来访问。
  2. 保护继承(protected): 父类的公有和保护成员将成为子类的保护成员。
  3. 私有继承(private):父类的公有和保护成员将成为子类的私有成员。

环状继承

C分别通过AB,继承了D两次,可能会发生冲突

class D{......};
class B: public D{......};
class A: public D{......};
class C: public B, public A{.....};

解决方案:虚继承(继承时在子类中维护一个指针,通过虚父类表访问共同的父类对象,而不是两个子类分别创建父类对象副本)

class D{......};
class B: virtual public D{......};
class A: virtual public D{......};
class C: public B, public A{.....};

例外

  1. 不能继承析构、构造、拷贝构造函数

  2. 不能继承重载的运算符

  3. 不能继承友元函数

  4. 构造函数调用顺序:基类 > 成员类 > 派生类;

  5. 多继承派生类: 基类构造顺序 依照 基类继承顺序调用

  6. 类成员:依照 类成员对象 定义顺序 调用成员类构造函数

重载

函数重载

运算符重载

image

Box是一个类class

单目运算符重载

Box operator++ ()      //重载++Box
Box operator++ (int)   //重载Box++
Box operator- ()       //重载-Box,取负数,
  1. 如果要实现++(++a) 就要返回Box&类型,因为返回Box类型是一个新对象,也就是说++a之后a并不会变!!!

如果不使用引用作为返回值,则会默认创建一个新对象并返回

  1. 不可以重载后置取负数,因为c++本身就不支持!!!

双目运算符重载

Box operator+(const Box&);             //定义为类成员函数
Box operator+(const Box&, const Box&); //普通通用函数

关系运算符重载

同单目

输入输出<< >>

一般要用到友元的非成员函数写,才可以保证cout>>Box b,不用friend,只能用传一个参数,第一个操作数则是当前类的对象
因此成员函数只能保证Box b >>cout

friend ostream &operator<<( ostream &output,
                                       const Box &B )
      {
         output << B.feet;
         return output;
      } //友元函数是为了保证可以访问到Box B的feet,因为他有可能是private

友元函数的声明通常放在类的定义内部,但是友元函数并不是类内函数!!!!

**在成员函数中,操作运算符必须是对象。而在<<操作符中,左操作符是ostream对象。所以就要使用友元函数进行定义 **

赋值

void operator=(const Box &B)   //函数体内依次赋值就行,可以实现用=直接初始化新对象 
operator int() {               //写在类内部,作用是重载int,可以Box b, int a = b隐式转换,n是b的成员变量
        return n;
    }

小括号

class MyFunction {
public:
    int operator() (int x, int y) {
        return x + y;
    }
};

int main() {
    MyFunction func;
    int result = func(1, 2);  // 调用重载的()运算符,效果等同于func.operator()(1, 2)
    return 0;

中括号

int& operator[](int i)

多态

C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数

常用API

cmath

  1. double cos(double)、sin、tan、log(ln)、sqrt、fabs(浮点绝对值)、floor(小于等于他的整数)
  2. double pow(double x, double y) 返回x的y次方

ctime、random

  1. time(NULL) 返回1970.1.1到现在经历的秒数
srand((unsigned) time(NULL)); /*播种子*/
int num = rand(); //如果要保留0-99,可以%100,即保留最后两位

string

  1. append() find() insert() length() substr()
posted @   __Zed  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示