首页 > 编程语言 >「REMAKE C++」Day 1

「REMAKE C++」Day 1

时间:2022-12-14 22:45:43浏览次数:39  
标签:const 函数 静态 C++ 编译器 REMAKE 内联 Day 指针

Day 1

  • 阅读了一些 《C++那些事儿》的基础知识
  • Google C++ 命名风格简单学习。

const 关键字

const 对象默认为文件内局部变量

// file1
int v;
extern const int cv;
// file2;
extern int v;
extern const int cv;
std::cout << v << endl;
std::cout << cv << endl;

可以发现未被 const 修饰的变量不需要extern显式声明!而 const 常量需要显式声明 extern ,
并且需要做初始化!因为常量在定义后就不能被修改,所以定义时必须初始化。

const指针,constexpr

  • const 类型的指针: type *const p = &x; const 值指针指的地址不能变,可以用 p 对 x 作修改。
  • 顶层 const 表示指针本身是个常量
  • 底层 const 表示指针所指的对象是一个常量
  • constexpr : 编译时确定是一个常量表达式,来替换 const 。
    • 与指针搭配,定义的指针将是常量指针(顶层 const),且不能指向在函数中定义的变量(地址不固定)。
  • 指向 const 类型的指针可以指向 非 const 类型的对象,但是不能直接修改对象。

static 关键字

声明的数据,空间将在程序生命周期分配

静态变量

静态变量: 函数中的变量,类中的变量

在函数中的静态变量:

  • 是每次调用函数共享静态变量

在类中的静态变量:

  • 初始化要在类的定义外通过 type Class_name::variable_name = value 来初始化。
  • 通常与类的实现写在一起。所有对象共享这个类的静态变量。

静态的类成员

类中的静态对象:

  • 在程序结束才会调用析构函数!

类中的静态成员函数:

  • 静态成员函数不依赖于类的对象。允许使用对象和'.'来调用静态成员函数。但建议使用类名和范围解析运算符调用静态成员。
    • Class_name::static_function_name();
  • 允许静态成员函数仅访问静态数据成员或其他静态成员函数,它们无法访问类的非静态数据成员或成员函数

static 用于限制访问范围

static 关键字声明的对象等,限定于本文件的全局变量

this 指针

this指针的使用:

  • 在类的非静态成员函数中返回类对象本身的时候,直接使用 return \*this
  • 参数与成员变量名相同时,如 this->n = n (不能写成n = n)。

this 指针相当于给函数传递了一个隐形参数。

  • this 指针会被编译器解析为 Class_name* const this

  • 而在 const 函数时,由于const函数只能访问const变量与函数,所以 this 指针是 const Class_name* const this

  • 另外,C++ 中 struct 与 class 只有一个区别,类的成员默认是private,而结构是public 。

inline

inline 特点

编译器对 inline 函数的处理步骤:

  1. 将 inline 函数体复制到 inline 函数调用点处;
  2. 为所用 inline 函数中的局部变量分配内存空间;
  3. 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中;
  4. 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 GOTO)。

内联能提高函数效率,但并不是所有的函数都定义成内联函数!内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。

  • 如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收货会更少!
  • 另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。

以下情况不宜用内联:

  • 如果函数体内的代码比较长,使得内联将导致内存消耗代价比较高。(感觉是空间要求高?)

  • 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

inline 与 虚函数

  • 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。
  • 内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。
  • 所以,inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 Base::who()),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。最终是否内联取决于编译器

    编译器能够清楚知道是哪个类的对象调用才可以内联,否则无法在编译期优化

virtual

虚函数与运行多态

  • 虚函数的调用取决于指向或者引用的对象的类型,而不是指针或者引用自身的类型。
class A {
public:
    virtual void say() {
        std::cout << "Hello A\n";
    }
};
class B : public A {
public:
    virtual void say() {
        std::cout << "Hello B\n";
    }
};
int main() {
    A* p = new B();
    p->say();   // 输出 Hello B,但 p 的原始类型是 A
    return 0;
}

vptr与vtable

  • 每个使用虚函数的类(或者从使用虚函数的类派生)都有自己的虚拟表
  • 虚拟表包含可由类的对象调用的每个虚函数的一个条目。此表中的每个条目只是一个函数指针,指向该类可访问的派生函数。
  • 虚表只是编译器在编译时设置的静态数组, 编译器还会添加一个隐藏指向基类的指针,我们称之为vptr。vptr在创建类实例时自动设置,以便指向该类的虚拟表。与this指针不同,this指针实际上是编译器用来解析自引用的函数参数,vptr是一个真正的指针。
  • 因此,它使每个类对象的分配大一个指针的大小。这也意味着vptr由派生类继承,这很重要。

虚表图

虚函数中默认参数

  • 默认参数是静态绑定的,虚函数是动态绑定的。 默认参数的使用需要看指针或者引用本身的类型,而不是对象的类型。
class A {
public:
    virtual void say(int x = 10) {
        std::cout << "Hello " << x << std::endl;
    }
};
class B : public A {
public:
    virtual void say(int x = 20) {
        std::cout << "Hello " << x << std::endl;
    }
};
int main() {
    A* p = new B();
    p->say();   // 输出 Hello 10。是 p 的原始类型中的默认参数
    return 0;
}

一些常见问题

  • 静态函数不可以声明为虚函数

    同时也不能被const 和 volatile关键字修饰

    虚函数依靠vptr和vtable来处理。vptr是一个指针,在类的构造函数中创建生成,并且只能用this指针来访问它,静态成员函数没有this指针,所以无法访问vptr。

  • 构造函数不可以声明为虚函数。同时除了inline|explicit之外,构造函数不允许使用其它任何关键字。

    尽管虚函数表vtable是在编译阶段就已经建立的,但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。 如果类含有虚函数,编译器会在构造函数中添加代码来创建vptr。 问题来了,如果构造函数是虚的,那么它需要vptr来访问vtable,可这个时候vptr还没产生。 因此,构造函数不可以为虚函数。

    我们之所以使用虚函数,是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的,实例的类型必须是明确的。 因此,构造函数没有必要被声明为虚函数。

  • 析构函数可以为虚函数

    析构函数可以声明为虚函数。如果我们需要删除一个指向派生类的基类指针时,
    应该把析构函数声明为虚函数。 事实上,只要一个类有可能会被其它类所继承, 就应该声明虚析构函数(哪怕该析构函数不执行任何操作)

  • 虚函数可以为私有函数

    基类指针指向继承类对象,则调用继承类对象的函数;
    int main()必须声明为Base类的友元,否则编译失败。 编译器报错: ptr无法访问私有函数。 当然,把基类声明为public, 继承类为private,该问题就不存在了

标签:const,函数,静态,C++,编译器,REMAKE,内联,Day,指针
From: https://www.cnblogs.com/Roshin/p/remake_cpp_day1.html

相关文章

  • C++ 模板
    函数模板基础建立一个通用的函数,将函数返回值类型和形参类型不具体指定,用一个虚拟的类型来表示template<typenameT>函数的声明和定义template---声明创建模板typen......
  • VC++判断网络连接状态
    在开发中,需要判断是否有网络连接,实现函数如下:#include<Wininet.h>BOOLCMFCDemoDlg::DoHaveInternetConnection(){BOOLbRet=FALSE;//如果函数返回FALSE,则肯定......
  • <一>C++ STL
    STL(standardtemplatelibaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。通俗来说:STL就是将常见的......
  • c++类型萃取
    判断两个类型的关系#include<iostream>#include<type_traits>usingstd::cout;usingstd::endl;//is_sameisusedtojudgetwodatatypessameornotduringcompili......
  • MYSQL 3 DAY
    目录MySQLday031、约束1.1、唯一性约束(unique)1.2、主键约束1.3、外键约束2、存储引擎?(整个内容属于了解内容)2.1、完整的建表语句2.2、什么是存储引擎呢?2.3、查看当前mysql......
  • C++ 通过 syscall 获取本线程 TID
    通过pthread_self及std::this_thread::getid函数获取的线程ID,跟使用top/htop命令呈现的线程ID不对应。通过如下代码获取跟top/htop一致的TID:#include<syscall.h>pi......
  • C++ 如果设置日期 & 时间基础篇
        ......
  • c++ 部署libtorch时对Tensor块的常用操作API
    一、前言使用pytorch可以很方便地训练网络,并且pytorch的官方网站中给出了很全的python对tensor的操作接口API,但是在部署libtorch的时候,c++对tensor的操作接口API资料甚少,......
  • 每日食词—day037
    lessonn. v.课程、功课、课loginn. v.登录、登入relativen. adj.相对、相对的、相关的CDNabbr.内容分发网络(ContentDistributionNetwork、ContentDel......
  • 每日食词—day036
    redundantadj.多余的、冗余的、冗余、过多的assignmentn.赋值、分配、指派、指定exceptionn.异常、例外muten. v. adj.静音、静音键provincen.省份......