杂记2

explicit

在 C++ 中,explicit 是一个关键字,用于修饰类的构造函数,其作用是禁止编译器将一个参数构造函数用于隐式类型转换。具体来说,当一个构造函数被 explicit 修饰时,只能通过显式调用来创建该类的对象,而不能通过隐式类型转换来创建对象。

下面通过一个例子来说明 explicit 关键字的作用:

class A {
public:
explicit A(int x) : m_x(x) {}
int getX() const {
return m_x;
} private:
int m_x;
}; void foo(A a) {
cout << a.getX() << endl;
} int main() {
A a1{1}; // 正确,使用显式调用构造函数创建对象
A a2 = 2; // 错误,隐式类型转换被禁止了
foo(3); // 错误,隐式类型转换被禁止了
foo(A{4}); // 正确,使用显式调用构造函数创建对象
return 0;
}

在上面的代码中,我们定义了一个类 A,其中构造函数被 explicit 修饰。我们在函数 foo 中使用了 A 类型的参数,然后在 main 函数中分别使用了显式调用构造函数和隐式类型转换来创建 A 类型的对象,并尝试将这些对象作为参数传递给函数 foo。

由于构造函数被 explicit 修饰,因此我们无法直接使用隐式类型转换来创建 A 类型的对象,也无法将一个整数隐式转换为 A 类型并作为参数传递给函数 foo。只有通过显式调用构造函数来创建对象,才能正常进行编译。

使用 explicit 关键字可以显式地指定哪些构造函数可以被用于隐式类型转换,从而避免了一些潜在的类型转换错误。使用 explicit 关键字还可以提高代码的可读性,使得代码更加易于理解和维护。

export

export关键字是C++20中引入的新特性,用于向编译器声明和导出模块接口。它用于定义模块的接口,可以将声明的标识符(函数、变量、类等)导出到模块外部,供其他模块使用。同时,它还可以限定一个模块内的标识符只能被本模块内的其他代码使用,而不能被导出到外部。

export关键字通常与import关键字一起使用,import用于导入其他模块的接口。

下面是一个使用export的示例:

// module1.cpp
export module module1; export int add(int a, int b) {
return a + b;
} export int value = 42; // main.cpp
import module1; int main() {
int result = add(2, 3);
std::cout << "result: " << result << std::endl;
std::cout << "value: " << value << std::endl;
return 0;
}

在上面的示例中,module1模块中导出了add函数和value变量,并在main.cpp中使用了这两个标识符。当编译main.cpp时,编译器会自动将module1模块编译成一个单独的文件,并将其链接到main程序中。

需要注意的是,export关键字目前还不是所有编译器都支持,具体情况需要查看编译器的文档。

typeid和decltype

C++中的typeid关键字用于获取一个表达式的类型信息,可以用来判断两个对象是否为同一种类型。具体来说,typeid可以返回一个type_info类型的对象,该对象包含了表达式的类型信息,可以通过type_info对象的name()方法获取类型名。typeid一般用于运行时类型识别(RTTI)。

RTTI运行时类型识别(Runtime Type Identification,RTTI)是一种在程序运行时确定对象类型的机制。RTTI通常用于C++中,可以使用typeid运算符来获得对象的类型信息。在程序中,通过将对象转换为其基类或接口类型来使用RTTI。RTTI可以用于在程序运行时进行类型安全的转换和异常处理,但过度使用RTTI可能会导致代码的性能下降。

与之相关的还有编译时类型识别(Compile-time type checking)

例如,下面的代码可以获取一个变量的类型信息,并输出其类型名:

#include <iostream>
#include <typeinfo> int main() {
int x = 42;
const std::type_info& type = typeid(x);
std::cout << type.name() << std::endl;
return 0;
}

输出结果为:

i

这里的i表示整数类型。另外,char型的类型值为c,long型的类型值为l,float型为f。简单类型是用的单个字符表示,类或函数用字符串表示,其中会包括返回值类型、参数类型等字符,如:

int fun(long x=0,char y='1'){
return 0;
}
int main(){
const std::type_info& type = typeid(fun);
std::cout << type.name() << std::endl;
}

第二个i是函数返回值类型,l是第一个参数类型值,c是第二个参数类型值。

另外,C++11中引入了decltype关键字,可以用来获取一个表达式的类型,也可以用于函数返回类型的推导等。

C++11中引入了decltype关键字,用于获取一个表达式的类型,也可以用于函数返回类型的推导等。decltype的基本语法如下:

decltype(expression)

其中,expression可以是任意一个表达式,decltype会返回该表达式的类型,包括const、引用等修饰符。

下面是几个使用decltype的例子:

int x = 42;
const int& y = x;
decltype(x) z1 = 0; // z1的类型为int
decltype(y) z2 = x; // z2的类型为const int&
decltype(x+y) z3 = 0; // z3的类型为int

在上面的代码中,z1的类型为int,因为decltype(x)的结果是intz2的类型为const int&,因为decltype(y)的结果是const int&z3的类型为int,因为decltype(x+y)的结果是int

另外,decltype还可以用于函数返回类型的推导。例如:

template <typename T, typename U>
auto add(T t, U u) -> decltype(t+u) {
return t + u;
}

在上面的代码中,decltype(t+u)用于推导函数返回类型,即tu相加的结果类型。

需要注意的是,decltype并不会对表达式进行求值,而是仅仅返回表达式的类型。因此,如果表达式中包含了函数调用或运算符重载等操作,decltype会返回对应的函数返回类型或运算符重载结果类型,而不是表达式的值。

typename

typename关键字则用于指明一个名称是类型名称。在C++中,有时需要使用嵌套的类型名称,如类模板中的类型别名或嵌套类的名称等。在这种情况下,需要使用typename关键字来指明名称是类型名称而非成员名称。例如:

template <typename T>
struct my_template {
typename T::value_type* ptr;
};

在上面的代码中,typename T::value_type表示T类型中的value_type类型,而不是T类型中的名为value_type的成员变量。如果省略了typename关键字,则编译器会将T::value_type解释为成员变量名,从而导致编译错误。

四种cast

在C++中,有四种类型转换的方式,它们分别是static_cast、dynamic_cast、const_cast和reinterpret_cast。

  1. static_cast:用于基本数据类型的转换,以及非多态类型的转换。例如,将int类型转换为double类型,或将指针类型转换为void*类型。

    如:

    int a = 10;
    double b = static_cast<double>(a); // 将int类型的a转换为double类型的b class Base {};
    class Derived : public Base {};
    Base* base = new Derived;
    Derived* derived = static_cast<Derived*>(base); // 将基类指针转换为派生类指针
  2. dynamic_cast:用于多态类型的转换,即具有虚函数的类型。它会在运行时检查是否能够安全地将一个指针或引用转换为目标类型,如果不能则返回NULL。例如,将基类指针转换为派生类指针,或将派生类指针转换为基类指针。

    class Base {
    public:
    virtual void print() { cout << "Base" << endl; }
    };
    class Derived : public Base {
    public:
    void print() { cout << "Derived" << endl; }
    }; Base* base = new Derived;
    Derived* derived = dynamic_cast<Derived*>(base); // 将基类指针转换为派生类指针
    if (derived) {
    cout<<"derived"<<endl; // 输出"Derived"
    }else{
    cout<<"error deriving";
    }
  3. const_cast:用于去除const属性。例如,将const int类型指针转换为int类型指针,或将const对象转换为非const对象。

    const int a = 10;
    int& b = const_cast<int&>(a); // 将const int类型的a转换为int类型的b的引用
    b = 20;
    cout << a << endl; // 输出10,因为a依然是const类型
  4. reinterpret_cast:用于进行二进制的低层次转换,不考虑类型之间的关系。例如,将一个指针转换为一个整数,或将一个整数转换为一个指针。

    int a = 10;
    int* p = &a;
    long long b = reinterpret_cast<long long>(p); // 将int类型的指针p转换为long long类型的b
    cout << b << endl; // 输出p的地址的十进制表示

需要注意的是,这些类型转换都具有一定的风险,需要谨慎使用。特别是reinterpret_cast,容易导致不可预测的错误,应该尽量避免使用。

memset

C++ 中的 memset 是一个用于填充内存块的函数,其定义在头文件 <cstring> 中。memset 可以将一段内存块的值都设置为指定的值,常用于清空数组或结构体等操作。

memset 函数的语法如下:

void* memset(void* ptr, int value, size_t num);

其中,第一个参数 ptr 是指向内存块的指针,第二个参数 value 是要设置的值(通常是 0 或 -1),第三个参数 num 是要设置的字节数。函数会将 ptr 指向的内存块中的前 num 个字节都设置为 value。

例如,下面的代码使用 memset 函数将一个数组清空:

int arr[10];
memset(arr, 0, sizeof(arr));

在上面的代码中,我们将 arr 数组中的所有元素都设置为 0。由于数组中有 10 个元素,因此我们传递给 memset 函数的第三个参数是 sizeof(arr),表示要设置的字节数。

需要注意的是,memset 函数并不会检查数组越界等错误,因此使用时需要确保不会访问到不属于自己的内存区域。此外,对于非 POD 类型(即含有构造函数、析构函数或虚函数的类型),使用 memset 函数可能会导致不可预期的行为,因此需要谨慎使用。

assert

assert断言,是C++<assert.h>库的函数,用来找出程序的错误的。格式:assert(exp);

assert的第一个参数是一个表达式,就是用来找错的表达式,如果为真则程序继续执行,若为假则引起abort中断信号,程序终止执行。

如:

#include<assert.h>
#include<iostream>
int main(){
int a=0;
assert(a); }

为什么不用if

assert是用来排除错误的,而if是用来找异常的,错误是可以通过修改去掉的,而异常是无法避免的。

为什么不直接cout

因为在一些大项目中,可以不止一个输出,所以如果找到错误,后续的程序便不需要继续执行。如:

#include<assert.h>
#include<iostream>
int main(){
int a=0;
int b=0;
int c=0;
//...
assert(a); std::cout<<a<<" ";
std::cout<<b<<" ";
std::cout<<c<<" ";
//...
}
使用规则
  • 根据上一条,所以assert一般用于程序输出的开始

  • 每个assert只检查一个条件,不然找到错误不知道是哪个

  • 不能改变环境的表达式

    如:assert(a++);这样会改变环境的表达式要用,只能用assert(a),assert(a<100)这样对原环境无影响的表达式

  • 一般assert()语句下一行空着,用来标注断言语句

C++温故补缺(二十一):杂项补充2的更多相关文章

  1. C温故补缺(二):volatile

    volatile 参考:CSDN volatile也是一个类型修饰符,被其修饰的变量意味着可以被某些编译器未知的因素修改,如操作系统,硬件,线程等. 当遇到volatile修饰的变量时,编译器对访问该 ...

  2. css杂项补充

    css杂项补充 一.块与内联 1.块 独行显示 支持宽高,宽度默认适应父级,高度默认由子级或内容撑开 设置宽高后,采用设置的宽高 2.内联 同行显示 不支持宽高 margin上下无效果,左右会起作用, ...

  3. 无废话ExtJs 入门教程二十一[继承:Extend]

    无废话ExtJs 入门教程二十一[继承:Extend] extjs技术交流,欢迎加群(201926085) 在开发中,我们在使用视图组件时,经常要设置宽度,高度,标题等属性.而这些属性可以通过“继承” ...

  4. Bootstrap <基础二十一>徽章(Badges)

    Bootstrap 徽章(Badges).徽章与标签相似,主要的区别在于徽章的边角更加圆滑. 徽章(Badges)主要用于突出显示新的或未读的项.如需使用徽章,只需要把 <span class= ...

  5. 【圣诞特献】Web 前端开发精华文章推荐【系列二十一】

    <Web 前端开发精华文章推荐>2013年第九期(总第二十一期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HTML5 和  ...

  6. Citrix 服务器虚拟化之二十一 桌面虚拟化之部署Provisioning Services

    Citrix 服务器虚拟化之二十一  桌面虚拟化之部署Provisioning Services Provisioning Services 是Citrix 出品的一系列虚拟化产品中最核心的一个组件, ...

  7. 二十一、contextMap中放的常用数据

    二十一.contextMap中放的常用数据 request:请求范围的数据.即ServletRequest中的那个Map parameters:请求参数的数据.即request.getParamete ...

  8. 牢记!SQL Server数据库开发的二十一条注意点

    如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL  Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS). ...

  9. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

  10. COJ 0979 WZJ的数据结构(负二十一)

    WZJ的数据结构(负二十一) 难度级别:C: 运行时间限制:5000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 请你实现一个数据结构,完成这样的功能: 给你一个 ...

随机推荐

  1. Three.js 进阶之旅:物理效果-3D乒乓球小游戏 🏓

    声明:本文涉及图文和模型素材仅用于个人学习.研究和欣赏,请勿二次修改.非法传播.转载.出版.商用.及进行其他获利行为. 摘要 本文在专栏上一篇内容<Three.js 进阶之旅:物理效果-碰撞和声 ...

  2. 深入理解Go语言中的sync.Cond

    1. 简介 本文将介绍 Go 语言中的 sync.Cond 并发原语,包括 sync.Cond的基本使用方法.实现原理.使用注意事项以及常见的使用使用场景.能够更好地理解和应用 Cond 来实现 go ...

  3. 11.getshell常见思路与技巧

    getshell常见思路与技巧 1.常规打点思路 信息收集: 绕开CDN找到所有靶标的真实IP 找到所有目标真实的C段 对所有的C段进行基础服务器的探测,端口的扫描.识别 对所有目标的子域名进行收集 ...

  4. GPT接入企微应用 - 让工作快乐起来

    引子 最近最火的莫过于ChatGPT了,在自己体验后就想着如何其他同事也能方便的起起来,毕竟独乐乐不如众乐乐,自己注册又是V-P-N,又是国外手机验证,对于大部分同事来说门槛还是高的.现在也有不少小程 ...

  5. ACM-NEFUOJ-P239回文数

    #include<bits/stdc++.h> using namespace std; int n,p[1000],len,p1[1000]; int f() { int i; for( ...

  6. 谷歌浏览器插件:FeHelper(WEB前端助手)

    背景 在现在的互联网时代,前端开发已经成为一个非常重要的领域.为了提高开发效率和质量,许多前端开发人员都喜欢使用一些相关工具来辅助他们的工作.而谷歌浏览器插件:WEB前端助手(FeHelper)就是其 ...

  7. SpringIOC注入

    在lagou的训练营的学习历程 SpringIOC实例化Bean的三种方式:1.使用无参构造器2.静态方法3.实例化方法.他要先实例化创建类(和2的区别),再调用. XML注入属性DI依赖注入,根据实 ...

  8. w10通过修改注册表实现禁止更新系统

    对于Windows系统更新或驱动更新,并不是越新越好,当然新版本的系统可能带来了许多新的功能.漏洞修补.漂亮的用户界面和流畅的系统优化等,但是新版本的系统和驱动更新有可能会造成CPU占用居高不下,文件 ...

  9. pandas之统计函数

    Pandas 的本质是统计学原理在计算机领域的一种应用实现,通过编程的方式达到分析.描述数据的目的.而统计函数则是统计学中用于计算和分析数据的一种工具.在数据分析的过程中,使用统计函数有助于我们理解和 ...

  10. [软件过程/软件生命周期模型]软件过程的工具链&技术链【待续】

    0 宣言:DevOps & RUP统一过程建模 1 项目管理 (需求管理 / 缺陷管理 / ...) 禅道(前身:bugfree) [在线协作] JIRA(项目与事务跟踪工具) 与禅道类同,但 ...