0%

牛客网刷题笔记(C/C++)篇

以下是在牛客网上刷C/C++专项练习时的知识点记录:

ASCII码对照表

  • ‘0’十进制是48
  • ‘A’十进制是65
  • ‘a’十进制是97

一个派生类继承了所有的基类方法, 但下列情况除外:(参考自https://www.runoob.com/cplusplus/cpp-inheritance.html)

  • 基类的构造函数, 析构函数和拷贝构造函数
  • 基类的重载运算符
  • 基类的友元函数

虚拟继承:

1
2
3
4
class D{......};
class B: public D{......};
class A: public D{......};
class C: public B, public A{.....};

这个继承会使D创建两个对象, 要解决上面的问题就要使用虚拟继承

1
2
3
4
class D{......};
class B: virtual public D{......};
class A: virtual public D{......};
class C: public B, public A{.....};

虚继承:
(在创建对象的时候会创建一个虚表)在创建父类对象的时候

拷贝构造函数:(参考自https://blog.csdn.net/lwbeyond/article/details/6202256)
拷贝构造函数是一种特殊的构造函数, 与普通构造函数的区别为, 它必须的一个参数是本类型的一个引用变量

调用拷贝构造函数的时机:

  • 对象以值传递的方式传入函数参数
  • 对象以值传递的方式从函数返回(函数执行到最后析构局部变量, 执行完析构返回的临时变量)
  • 对象需要通过另外一个对象进行初始化

浅拷贝和深拷贝

  • 默认拷贝构造函数
    默认拷贝构造函数没有处理静态数据成员
  • 浅拷贝
    默认拷贝构造函数就是浅拷贝, 浅拷贝对于动态分配的内容没有进行正确的操作, 即拷贝时只确保两个指针的值相等, 但是空间并没有复制
  • 深拷贝
    深拷贝对于对象中的动态成员重新动态分配空间

防止默认拷贝发生的方法:
声明一个私有拷贝构造函数, 不用定义, 当用户试图按值传递或函数返回该类对象, 将得到一个编译错误, 从而可以避免按值传递或返回对象.

拷贝构造函数的几个问题:

  • 拷贝构造函数类同于构造函数, 能调用private成员变量
  • 对于一个类X, 如果一个构造函数的第一个参数是下列之一, 且没有其他参数或其他参数都有默认值, 那么这个函数是拷贝构造函数
    • X &
    • const X &
    • volatile X &
    • const volatile &
  • 类中可以存在超过一个拷贝构造函数; 如果一个类中只存在一个参数为X &的拷贝构造函数, 那么就不能使用const Xvolatile X的对象实行拷贝初始化; 默认拷贝构造函数的参数可能为X::X(const X &)X::X(X &), 由编译器根据上下文决定选择哪一个

重载运算符:(参考自https://www.runoob.com/cplusplus/cpp-overloading.html)
重载的运算符是带有特殊名称的函数, 函数名是由关键字operator 和其后要重载的运算符符号构成
与其他函数一样, 重载运算符有一个返回类型和一个参数列表

不可重载的运算符:

  • . 成员访问运算符
  • .*, ->* 成员指针访问运算符
  • :: 域运算符
  • ?: 条件运算符
  • # 预处理符号

类重载, 覆盖, 重定义之间的区别:

  • 重载指的是函数具有的不同的参数列表, 而函数名相同的函数; 重载要求参数列表必须不同, 比如参数的类型不同, 参数的个数不同, 参数的顺序不同; 如果仅仅是函数的返回值不同是没办法重载的, 因为重载要求参数列表必须不同(发生在同一个类里)
  • 覆盖是存在类中, 子类重写从基类继承过来的函数; 被重写的函数不能是static的; 必须是virtual的; 但是函数名, 返回值, 参数列表都必须和基类相同(发生在基类和子类)
  • 重定义也叫做隐藏, 子类重新定义父类中有相同名称的非虚函数(参数列表可以不同)(发生在基类和子类)

友元函数:(参考自https://blog.csdn.net/qq_26337701/article/details/53996104)
友元函数是定义在类外的普通函数, 只是在类中声明该函数可以访问类中的private或者protected成员
友元函数声明的一般形式: friend <返回类型> <函数名> (<参数列表>);
注意:

  • 由于该函数并不是类的成员函数, 其声明可以放在类的私有部分, 也可放在共有部分
  • 一个类中的成员函数可以是另外一个类的友元函数, 而且一个函数可以是多个类的友元函数
  • 友元函数访问类中的私有成员和其他数据需要通过对对象进行引用
  • 友元函数在调用上同一般函数一样, 不必通过对对象进行引用

友元类:(参考自https://www.cnblogs.com/zhuguanhao/p/6286145.html)
友元类的所有成员函数都是另一个类的友元函数
友元类声明的一般形式: friend class <类名>;
注意:

  • 友元关系不能被继承
  • 友元关系是单向的, 不具有交换性
  • 友元关系不具有传递性

关于友元:

  • 通常将友元声明成组地放在类定义的开始或结尾
  • 不必预先声明类和非成员函数来将它们设为友元
  • 优点:
    • 可以灵活地实现需要访问若干类的私有或受保护的成员才能完成的任务
    • 便于与其他不支持类概念的语言(如C语言, 汇编等)进行混合编程
    • 通过使用友元函数重载可以更自然地使用C++语言的IO流库
  • 缺点:
    • 一个类将对其非公有成员的访问权限授予其他函数或者类, 会破坏该类的封装性, 降低该类的可靠性和可维护性

volatile:(参考自https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777432.html)
遇到volatile关键字声明的变量, 编译器对访问该变量的代码不再进行优化, 从而可以提供对特殊地址的稳定访问
volatile指出变量的值是随时可能发生变化的, 当要求使用volatile声明的变量的值的时候, 系统总是重新从它的所在的内存读取数据, 即使它前面的指令刚刚从该处读取过数据, 而且读取的数据立刻被保存

volatile的使用:

  • 中断服务程序中修改的供其它程序检测的变量需要加volatile
  • 多任务环境下个任务间共享的标志应该加volatile
  • 存储器映射的硬件寄存器通常也要加volatile说明, 因为每次对它的读写都可能有不同的意义
  • 内嵌汇编操纵栈(编译无法识别的变量改变)

volatile指针:

  • 修饰由指针指向的对象, 数据是const或volatile的:
1
2
const char* cpch;
volatile char* vpch;
  • 指针自身的值(一个代表地址的整数变量)是const或volatile的:
1
2
char* const cpch;
char* volatile vpch;

volatile注意:

  • 可以把一个非volatile int赋给volatile int, 但是不能把非volatile 对象赋给volatile 对象
  • C++中一个有volatile标识符的类只能访问它接口的子集, 一个由类的实现者控制的子集; 用户只能用const_cast来获得对类型接口的完全访问; 此外,volatile向const一样会从类传递到它的成员

const_cast:(参考自https://www.cnblogs.com/ider/archive/2011/07/22/cpp_cast_operator_part2.html)
C++提供了四个转换运算符用于进行指针和引用的转换:

  • const_cast <new_type> (expression)
  • static_cast <new_type> (expression)
  • reinterpret_cast <new_type> (expression)
  • dynamic_cast <new_type> (expression)
  • 一般可以将这四个标准转换运算符看作对传统转换的统一, 但编译器会做更多的处理, 特别是dynamic_cast不能用传统转换方式来完全实现

const_cast转换符是用来移除变量的const或volatile限定符

1
2
3
4
5
# 以下的代码并不会更改constant的内容, 尽管 &constant 和 modifier 指向了同一个地址
const int constant = 21;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;

C++对于指针的转换是任意的, 它不会检查类型, 任何指针之间都可以进行相互转换

C++运算符重载之类型转换运算符:(参考自https://blog.csdn.net/small_prince_/article/details/80518982)

转换构造函数(特殊的构造函数, 属于隐式转换):

  • 有且仅有一个参数
  • 参数是基本类型
  • 参数是其他类型
  • 可以普通类型到自定义类, 类到类

如何阻止编译器隐式类型转换?
C++提供了explicit关键字杜绝编译器的转换尝试, 转换构造函数被explicit修饰时只能进行显示转换

使用类型转换函数operate Type();(Type是要转换成的类型, 属于显示转换):

  • 类型转换函数与转换构造函数具有同等的地位
  • 使得编译器有能力将对象转化为其他类型
  • 编译器能够隐式地使用类型转换函数
  • 可以自定义类到普通类型, 类到类
Thank you for your reward !