首页 > 编程语言 >C++对象模型:object

C++对象模型:object

时间:2024-11-01 20:24:51浏览次数:2  
标签:object 模型 float C++ public void Bear class

一、object

typedef struct{
  float x;
  float y;
  float z;
}Point3d;

可以有以下方法打印上述类型字段:

  1. 定义函数
void print_point3d(const Point3d* pd){
  printf("(%g,%g,%g)", pd->x, pd->y, pd->z);
}
  1. 若要更有效率,可以定义一个宏函数
#define Point3d_print(pd) \
  printf("(%g,%g,%g)", pd->x, pd->y, pd->z);
  1. 也可以由一个前置处理宏来处理
#define X(p,xval) (p.x)=(xval)
//...
Point3d pd;
X(pd, 0.0);

在C++中,Point3d的实现方式如下:

  1. 可以采用独立的抽象数据类型ADT实现
class Point3d{
public:
  Point3d(float x=0.0, float y=0.0, float z=0.0):x_(x),y_(),z_(){}
  float x(){ return x_; }
  float y(){ return y_; }
  float z(){ return z_; }
  void x(float xval){ x_= xval; }
private:
  float x_;
  float y_;
  float z_;
};
inline ostream& operator<<(ostream& os, const Point3d& pt){
  os<<pt.x()<<pt.y()<<pt.z();
}
  1. 或使用多层的class层次结构实现
class Point{
public:
  Point(float x=0.0):x_(x){}
  float x(){ return x_; }
  void x(float xval){ x_=xval; }
private:
  float x_;
};

class Point2d:public Point{
public:
  Point2d(float x=0.0, float y=0.0):Point(x), y_(y){}
  float y(){ return y_; }
  void y(float yval){ y_=yval; }
private:
  float y_;
};

class Point3d:public Point2d{
public:
  Point3d(float x=0.0, float y=0.0, float z=0.0):Point2d(x,y),z_(z){}
  float z(){ return z_; }
  void z(float zval){ z_=zval; }
private:
  float z_;
};
  1. 更进一步,不管那种类型,都可以被参数化,坐标类型参数化
template<class type>
class Point3d{};

// 或如下,坐标类型和个数都参数化
/*
template<class type, int dim>
class Point{
public:
  Point();
  Point(type coords[dim]){
    for(int index=0; index<dim; index++){
      coords_[index]= coords[index]
    }
  }
  type& operator[](int index){
    assert(index<dim && index>=0);
    return coords_[index];
  }
  type& operator[](int index)const{
  }
private:
  type coords_[dim];
};
inline template<class type, int dim>
ostream& operator<<(ostream& os, const Point<type,dim>& pt){
  // print
}
*/

对于C++的封装,是否有布局上的成本

  1. 每个non-inline只会有一个函数实例,而每个拥有零个或一个定义的inline function则会在每个使用者中产生一个函数实例

  2. C++布局和效率的负担主要在virtual关键字上

  • 虚函数virtual function机制
    用以支持执行期绑定runtime binding
  • 虚继承virtual base class机制
    用以实现多次出现在继承体系中的基类,有单一且共享的实例

此外,还有一些多重继承下的额外负担,发生在“一个子类和其第二或后继的父类间的转换”之间

二、C++对象模型

class Point{
public:
    Point(float xval);
    virtual ~Point();

    float x()const;
    static int PointCount();
protected:
    virtual std::ostream& print(std::ostream& os)const;
    float x_;
    static int point_count_;
};

简单对象模型

一个object对应一系列的slots,每个slot指向一个member

表格驱动对象模型

C++对象模型

  • 非静态成员变量

  • 静态成员变量

  • 静态和非静态成员函数

  • 虚函数
    类生成一系列指向虚函数的指针,放在虚函数表vtbl
    每个对象有一个指向vtbl的指针vptrvptr的设置和重置由每一个类的构造函数,析构函数和赋值运算符自动完成
    每个类所关联的type_info object(用以支持RTTI),也由vtbl指出,通常放在第一个slot
    vptrvtbl的使用沿用cfront编译器

  • 加上继承

    1. 单一继承
    class Library_materials{};
    class Book:public Library_materials{};
    class Rental_book:public Book{};
    
    1. 多重继承
    class iostream:
      public istream, public ostream{};
    

    继承关系可以指定为虚继承

    class istream:virtual public ios{};
    class ostream:virtual public ios{};
    

    保证了在继承关系中只会存在一个基类的实例subobject

    C++最初采用的继承模型并不运用任何间接性:基类的成员变量直接放在子类对象中
    C++2.0引入的虚继承,为每个关联的虚基类加上指针

三、关键字差异

若没有C的8种整数需要支持,overloaded function的解决方案会更简单
若丢弃C的声明语法,就无需花时间判断下面到底是函数调用还是声明

void (*pf)(1024);
void (*pq)();

struct关键字实现了C的数据抽象观念,class关键字实现的是C++的ADT观念
将单一数组放在struct的尾端,于是每个struct对象将拥有可变大小的数组

struct M{
  char pc[1];
};

int main(){
  const char* str="";
  struct M* m=(struct M*)malloc( sizeof(struct M)+strlen(str)+1 );
  strcpy(m->pc, str);

  return 0;
}

四、对象差异

C++程序设计范式programming paradigms
程序模型procedural model
抽象数据类型模型ADT model
面向对象模型object oriented model

完成多态polymorphic操作

class Library_materials{
public:
  void check_in(){ std::cout<<"Library_materials"<<std::endl; }
};
class Book:public Library_materials{
public:
  void check_in(){ std::cout<<"Book"<<std::endl; }
};

Library_materials thing1;
Book b;
thing1= b;
thing1.check_in(); // 实际调用 Library_materials::check_in()

通过pointer或reference的间接处理实现多态,前提:check_in()是虚函数
thing1的定义反映的是ADT paradigm的行为

需要多少内存才能实现一个class object,一般而言包含如下:

  • 非静态成员变量大小
  • 由于alignment的需要而填补padding的空间,即对齐
  • 为了支持virtual而产生的额外负担
class ZooAnimal{
public:
  ZooAnimal(){}
  virtual ~ZooAnimal(){}
  virtual void rotate(){}
protected:
  int loc;
  char* name;
};

不同的指针类型,仅在于寻址大小不同,void*的寻址大小是未知的,这也是为什么空指针不能访问对象

加上多态

class Bear:public ZooAnimal{
public:
  Bear(){}
  ~Bear(){}
  void rotate(){}
  virtual void dance(){}
protected:
  enum class Dances{};
  Dances dances_known;
  int cell_block;
};

定义变量

Bear b;
ZooAnimal* pz= &b;
Bear* pb= &b;

包含虚函数的类,在实例化前需要实现虚函数,否则提示undefined reference to vtable for Bear
指针pzpb都指向对象b的第一个字节,差别是pb涵盖的地址包含整个Bear object,而pz涵盖的地址只包含Bear object中的ZooAnimal subobject

pz想访问Bear中的数据
经过一个显式的转换

(static_cast<Bear*>(pz))->dance();

或,更好的使用runtime operator,成本较高

if(Bear* pb2=dynamic_cast<Bear*>(pz)){
  pb2->dance();
}

若调用

pz->rotate()

pz的类型在编译期决定,其类型决定rotate()所调用的实例
类型信息的封装并不是维护在pz中,而是维护在link中,此link存在于object的vptrvtbl之间

Bear b;
ZooAnimal za= b;
za.rotate();

为什么za.rotate()调用的是ZooAnimal::rotate(),而不是Bear::rotate()
za只能是一个ZooAnimal,多态的潜在力量不会出现在直接存取object这件事上

若初始化函数将一个object内容完整拷贝到另一个object中,那么为什么za不指向Bearvtbl
因为编译器会确保,若object含有一个或多个vptr时,则那些vptr的内容不会被base class object初始化或改变

一个pointer或reference之所以支持多态,是因为他们并不引发内存中的任何与类型有关的内存委托操作,改变的只是其所指向的内存的大小和内容的解释方式而已

标签:object,模型,float,C++,public,void,Bear,class
From: https://www.cnblogs.com/sgqmax/p/18521182

相关文章

  • C++多线程:atomic
    在许多为了性能和效率的场景下,需要开发一些lock-free的算法和数据结构atomic_flag原子布尔类型,只支持test-and-set和clear操作构造函数atomic_flag()noexcept=default;atomic_flag(constatomic_flag&)=delete;只有默认构造函数,而不能从其他对象构造atomic_flag对象需......
  • C++对象模型:constructor
    构造函数constructorexplicit的引入,是为了能够制止“单一参数的constructor”被当作一个conversion运算符带有默认构造函数的对象成员若一个类中包含对象成员,且该对象有默认构造函数,此时:若该类没有构造函数则编译器会合成一个默认构造函数,且发生在真正调用时若该类有构造函......
  • 深入解析 Transformers 框架(三):Qwen2.5 大模型的 AutoTokenizer 技术细节
    前面2篇文章,我们通过查看Transformers包代码,学习了Transformer包模块API设计、模型初始化和加载流程:第1篇:transformers推理Qwen2.5等大模型技术细节详解(一)transformers包和对象加载第2篇:transformers推理Qwen2.5等大模型技术细节详解(二)AutoModel初始化......
  • 【C++】类与对象(中)
    1.类的默认成员函数  默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。一个类,我们不写的情况下编译器会默认生成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,我们稍微了解⼀下即可。其次就是C++11以后......
  • C++多线程:thread
    进程与线程进程:系统资源分配的最小单元,通常被定义为一个正在运行的程序实例线程:系统任务调度的最小单元进程间通信:管道,信号量,信号,消息队列,共享内存,套接字线程间通信:锁机制,信号量机制,信号机制,屏障同步:保证任务片段的先后顺序互斥:为了保证资源在同一时刻只能被一个线程使用,即......
  • C++多线程:mutex
    互斥量C++11互斥锁定义在<mutex>头文件中,提供了独占资源的特性C++11头文件中定义的互斥量互斥量说明mutex基本互斥量recursive_mutex递归互斥量timed_mutex定时互斥量recursive_timed_mutex递归定时互斥量std::mutex最基本的互斥量,提供了独占所有权......
  • C++多线程:condition_variable
    条件变量类似于pthread库中的pthread_cond_*()提供的功能,C++11标准提供了两种表示条件变量的类,分别是condition_variable和condition_variable_any,定义在头文件<condition_variable>中std::condition_variable当std::condition_variable对象调用wait()时,会阻塞当前线程,直到该s......
  • C++多线程:promise
    头文件包含:Providers类std::promisestd::packaged_taskFutures类std::futurestd::shared_futureProviders函数std::async()其他类型std::future_errorstd::future_errcstd::future_statusstd::launchstd::promise用来保存某一类型T的值,该值可以被future对......
  • C++多线程:package_task
    std::packaged_taskstd::packaged_task包装一个可调用对象,并允许获取该可调用对象计算的结果,可调用对象内部包含两个基本元素:1.被包装的任务任务是一个可调用对象,如函数指针或函数对象,该对象的执行结果会传递给共享状态2.共享状态用于保存任务的返回结果,并可通过future对象异......
  • 模拟大模型训练时,单双精度输出不一致?从而加剧幻觉?或导致幻觉?
        下面是Python代码。就同样的随机数据,分别在单精度、双精度下做模拟训练与预测,最后比较它们预测的值,发现不一致。    大家看看,代码是否有问题?importnumpyasnpimporttensorflowastffromtensorflow.keras.layersimportDense,LSTMfromtensorfl......