//64位系统
class A{ }; //sizeof(A)为1
class B : virtual public A{ }; //sizeof(B)为8
class C : virtual public A{ }; //sizeof(C)为8
class D : public B, public C{ }; //sizeof(D)为16
//sizeof(A)为1是因为编译器会安插一个char,使得多个object会有不同的地址
内存布局:

造成B和C大小为8的原因如下:
nonstatic data members和virtual nonstatic data members都存与class object中,且没有强制定义其排列顺序;static data members存于global data segment,不影响class object大小
nonstatic data members在class object中同一个access level的内存排列顺序应和被声明的顺序相同,不受static data members影响
class object的同一个access section中members不一定非得连续排列,member的alignment和内部使用的data members可能会介于声明的members间;且多个access section中data members可以自由排序,不用考虑声明顺序
access sections的多少并不影响内存大小
class A
{
public:
...
private:
float x;
static int y;
private:
float z;
static int i;
private:
float j;
}
extern float x;
class A
{
public:
A(float, float, float);
float X() const { return x; };
private:
float x, y, z;
}
放在现在,X()的返回值肯定是class内部那个,但在以前的编译器,此操作会返回extern那个。因此,这也就产生了两种防御性程序风格:
将所有data member放于class声明最开始处
class A
{
private:
float x, y, z;
public:
//这样将保证class内部
float X() const { return x; };
}
将所有inline member functions,放于class外。inline函数实体,在整个class声明完全看见后,绑定操作才会进行
class A
{
public:
A();
private:
float x,y,z;
};
inline float A::X() const
{
return x;
}
请思考如下代码:
typedef int length;
class A
{
public:
//length被判定为int类型
//_val 判定为A::_val
void do1( length val ) { _val = val; };
length do1() { return _val; };
private:
//这里length必须在"本class对它的第一个操作前"被看见.否则先前的判定操作不合法
typedef float length;
length _val;
}
对于member function的argument list来说,argument list中的名称会在它们第一次遭遇时被适当判断完成。因此,需要将nested type声明放于判断前
现有如下代码:
A a;
//x的存取成本?
a.x = 0.0;
A* ot = &A;
//通过指针的x的存取成本?
pt->x = 0.0
class object里的static data member,对于class objects和其本身,都不会产生额外负担
无论是复杂的继承关系还是单一的class object,static data member永远只有一个实例
static data member每次被取用时,编译器都会对其进行转化
//a.i = 0;
A::i = 0;
//pt->i = 0;
A::i = 0;
多个相同的classs都声明相同的static member,在data segment中这肯定会导致名称冲突,但编译器对其进行name-mangling,也就是暗中对每一个冲突的static data member编码,如此即可获得独一无二的识别代码
对于以上代码,虽然使用的member selection operators对static data member进行存取操作,但这只是图方便,实际上static data member并不在class object中,因此也并没有通过class object
若由A中的一函数调用static data member,会发生如下转化:
//do为A中的函数
do().i = 0;
//转化求值
(void) do();
A.i = 0;
若取static data member地址,也只会得到指向其类型的指针,并不会指向其class member
&A::i;
//转化
const int*
nonstatic data members存放在class object中,需经过explict或implicit class object进行存取,且进行存取操作时,编译器还需要把class object的起始地址加上data member的offset
A A::do1( const A& pt )
{
x += pt.x;
y += pt.y;
z += pt.z;
}
//转化
A A::do1( A* const this, const A& pt )
{
this->x += pt.x;
this->y += pt.y;
this->z += pt.z;
}
--------------------------------------------分割线--------------------------------------------------------
a.y = 0.0;
//起始地址+offset
&a + (A::y - 1);
这里的"-1"操作是因为指向data member的指针的offset总是被加上1,如此编译系统即可区分"指向data member的指针,用以指出class的第一个member"和"指向data member的指针,没有指向任何member"两种情况
class B
{
public:
virtual ~B();
protected:
static B origin;
float x,y,z;
}
//&origin: 当前地址减去offset并加一
float B::* p1 = &origin.y;
//最终得到val:offset + 1
float B::* p2 = &B::x; //B::* 是指向B data member的指针
因为offset的值于编译期即可得出,因此存取一个nonstatic data member其实效率和c struct member一样,派不派生也是如此
现有以下代码:
class Point2d
{
public:
float x() { return _x; }
float y() { return _y; }
void operation+=( const Point2d& rhs )
{
_x += rhs.x();
_y += rhs.y();
}
... //constructor
private:
float _x, _y;
}
class Point3d
{
public:
float z() { return _z; }
void operation+=( const Point3d& rhs )
{
Point2d::operator+=( rhs );
_z += rhs.z();
}
...//constructor
private:
float _z;
}
以上这种继承被称为具体继承(concrete inheritance),derived class继承base class的data member和 member function,将之局部化,但这种行为并不会增加空间和时间上的额外负担。没有virtual function时,布局其实和c struct一样
对于具体继承,需要注意因alignment padding膨胀的空间
//32位
//A大小 4 + 1 + alignment 3
class A
{
private:
int val;
char c1;
}
//B大小由8 + 1 + aligment3
class B : public A
{
private:
char c2;
}
//根据B的意思,C也就是16
class C : public B
{
private:
char c3;
}
也许你会认为,这不是浪费很多空间吗,为什么不让derived class member直接填上base class aligment那一部分?

如果是以上布局,又会产生一个问题:继承而得的members会被覆盖
B* pb;
A* pa1, pa2; //可指向ABC
pa1 = pb;
//这将导致c2的值被覆盖掉
*pa2 = *pa1;
现有如下代码:
class Point2d
{
public:
float x() { return _x; }
float y() { return _y; }
virtual void operation+=( const Point2d& rhs )
{
_x += rhs.x();
_y += rhs.y();
}
virtual float z() { return 0.0; }
virtual void z(float) { }
... //constructor
private:
float _x, _y;
}
class Point3d
{
public:
float z() { return _z; }
void operation+=( const Point2d& rhs )
{
Point2d::operator+=( rhs );
_z += rhs.z();
}
...//constructor
private:
float _z;
}
//p1和p2可能为Point2d类型,也可能为Point3d类型
void do( Point2d& p1, Point2d& p2 )
{
p1 += p2;
}
现有如下代码:
class Point2d
{
public:
... //含有virtual函数
protected:
float _x, _y;
}
class Point3d : public Point2d
{
...
protected:
float _z;
}
class Vertex
{
public:
... //含有virtual函数
protected:
Vertex* next;
}
class Vertex3d : public point3d, public Vertex
{
...
protected:
float mumble;
}
Vertex3d v3d;
Vertex* pv;
Point2d* p2d;
Point3d* p3d;
pv = &v3d;
//内部转换 pv = (Vertex*)( ( (char*)&v3d ) + sizeof(Point3d) );
//无需转换
p2d = &v3d;
p3d = &v3d
Vertex3d* pv3d;
Vertex* pv;
//若想进行指针的指定操作,还需加个判断
pv = pv3d ? (Vertex*)((char*)pv3d) + sizeof( Point3d ); //pv3d可能为野指针
内存布局:

iostram library:
//对应如下左图
class ios {...};
class istream : public ios {...};
class ostream : public ios {...};
class iostream : public istream, public ostream {...};
//对应如下右图
class ios {...};
class istream : virtual public ios {...};
class ostream : virtual public ios {...};
class iostream : public istream, public ostream {...};

根据如上可知,虚拟继承可以解决存储多个同一base class的问题(ios),那么这是如何实现的呢?
编译期实现策略:
class Point2d
{
...
protected:
float _x, _y;
}
class Point3d : virtual public Point2d
{
...
protected:
float _z;
}
class Vertex : virtual public Point2d
{
...
protected:
Vertex* next;
}
class Vertex3d : public Vertex, public Point3d
{
...
protected:
float mumble;
}
一般的布局策略是先安排derived class不变部分,随后建立共享部分
存取class的共享部分:在每一个derived class object中安插一些指针,每个指针指向一个virtual base class

void Point3d::operator+=( const Point3d& rhs )
{
_x += rhs._x;
_y += rhs._y;
_z += rhs._z;
}
//进行如下转换
__vbcPoint2d->_x += rhs.__vbcPoint2d->_x;
_z += rhs._z;
----------------------------------分割线-------------------------
Point2d* p2d = pv3d;
//进行如下转换
Point2d* p2d = pv3d ? pv3d->__vbcPoint2d : 0;
然而,这种实现模型却存在两个缺点:
解决:
对于第一个,引入virtual base class table,virtual base class指针放在table中,编译期会安插一个指针指向virtual base class table
对于第二个,拷贝取得所有的nested virtual base class指针
virtual base class最有效的形式:一个抽象virtual base class,不含data member
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调