
也就是当新的对象被创建后,该对象所属的类的构造函数自动被调用,你新建的对象就被初始化了,打比方:
class CStudent
{
public:
CStudent()
{
Snum=0;
Password=0;
}
private:
int Snum;
int Password;
};
当你创建一个CStudent的对象时,CStudent student;
系统自动调用类函数CStudent()
这时studentSnum=0,studentPassword=0
MyClass a:定义一个对象,调用1次构造函数。
b[2]:定义含有两个元素的数组,实际上定义两个对象,所以调用2次构造函数。
c:定义指向类对象的指针,但没有进行实际的内存分配,所以不调用构造函数。
d:定义指向类对象指针的指针,但没有进行实际的内存分配,所以不调用构造函数。
e[2]:定义含有两个指向类对象的指针的数组,但没有进行实际的内存分配,所以不调用构造函数。
f=new char A:这个语句有问题,f是指向MyClass的对象,你却让它指向char类型的变量。应该改成:f=new MyClass,涉及到内存的分配,调用1次构造函数。
g=new MyClass [2],涉及到内存的分配,调用2次构造函数。
总结:只有涉及到内存分配的时候,才会调用构造函数。普通对象调用一次,对象数组调用N次(该数组含有N个元素)。只定义指针而不给指针赋值,则不涉及内存分配,不调用构造函数。
对象
对象对象是已命名的数据集合。其中“已命名的数据”指的就是它的属性和方法,如:
var a = { val : 1 , func:function(){}}对象 a 拥有两个已命名的数据 val 和 func 。
访问对象的成员可以使用 或 [ ] ,其中 运算符要求它右边必须是合法的标示符,而 [ ] 运算符,对它的 *** 作数并无要求,任何合法的值都可以,如:
aval ; // 1afunc() ; // undefined ;
a["x"] = 2 ; // a : { val : 1 , func:function(){} , x : 2 }
a[null] = 3 ; // a : { val : 1 , func:function(){} , x : 2 ,null : 3}
2原型
对象有一个私有的属性 __proto__,持有其原型的引用。
对象的原型,也是一个对象。对象与它的原型的关系就是:数据共享,这体现在对象属性值的查找过程。访问对象的属性,首先在它自定义的数据集合中寻找,找到后,立即返回该值;如果没有找到,则到它的原型中寻找,找到后,立即返回值;;如果一直到原型链(原型还可以有原型)的末端都没有找到,则返回 undefined。
var b = { bPro : "in b" } ;a__proto__ = b ;
alert( abPro ) ; // in b ;
如果一个对象没有显式的指定原型,那么它的原型默认是 Objectprototype ,而 Objectprototype__proto__ = null ,所以它就是所有原型链的结尾。
为对象添加了一个在原型中存在的属性,则会覆盖对原型属性的访问。
abPro = "in a" ;alert(abPro) ; // "in a"
alert(a__proto__bPro) ; // "in b"
可以看到,修改对象,并未影响到原型,更不会影响到共享同一个原型的其他对象。
3 构造函数
任何函数都可以用作构造函数,只要使用 new 运算符进行调用即可。
构造函数的原理,就是创建一个对象,并将函数的属性 prototype 赋给刚刚创创建的这个对象的 __proto__ 属性、传递原型,然后将 this 指向它;执行函数体,函数体中形如 thisx = y 的代码都是在给这个刚创建的对象添加成员;最后这个新创建的对象会作为 new 表达式的值返回。如下:
function obj(xValue){thisx = xValue ;
}
objprototype = { pro : "in prototype" } ;
var a = new obj(1) ; // { x : 1 }
alert(apro) ; // "in prototype"
a__proto__ === objprototype // true;
当然,函数也可以返回值。但只有 Object 类的实例(包括子类实例)才会生效,其他的值都会被忽略,继续返回刚创建的对象(即使函数体中一个 this 都么有)。
function func( ret ){thisx = 5 ;
return ret;
}
var a = new func() ; // { x : 5 }
var b = new func( null ) ; // { x : 5 }
var c = new func( { } ) ; // { }
var d = new func( [] ) ; // []
var e = new func( 1 ) ; // { x : 5 }
var f = new func( new Number( 1 ) ) ; // Number
4 Bulalalal
后面的我没看懂,查了查,发现内容也没啥新鲜的。
如果一个拥有返回值,且返回的是 Object 类的实例(包括子类实例),那么使用不使用 new 运算符的结果都是一样的。同其他语言中一样,具有这样功能函数,都叫做工厂函数,更复杂的创建过程可能不叫这个名字,但也一定会包括"工厂"二字。
function obj(x , y ){return { proX : x , proY : y } ;
}
虽然说懂了原理,其他的都是形式问题。但其实我也觉得有必要深入了解一下,毕竟和人交流的时候,不能让一些其实没多大意义的名词给难住、影响沟通效果。
只是我有点累了,手指头快麻了,我去瞅瞅别的东西了。(还有,我已不做程序员好多年,新东西学不动了)
参考文章:对象 http://wwwtooteinet/archives/365#2-1 推荐你将函数那一章也看看。
创建对象的时候不一定非要用new
Calendar rightNow = CalendargetInstance();
中 getInstance 函数的功能是 返回一个 Calendar 类型的对象
类的使用 基本上和其他基本类型 如int ,string 用法差不多
常量对象必须初始化(从常量这个词的语义上去考虑)。也就是定义常对象的同时就要给对象赋初值,对象的初始化是通过构造函数完成的,如果构造函数不需要参数的话,那么可以不必显式调用,如果对象初始化时要求有参数,要么必须传参。调用的构造函数与普通的对象一样。构造函数采不采用初始化列表,,跟赋值没有关系。初始化列表主要原因一是继承,初始化基类,二是效率,当然这个效率指的是当类中有类对象时,用初始化列表,可以减少函数调用,还有就是LS说的,因为常量只能被初始化,不能被赋值,作为类的成员只能在初始化列表中初始化。
与类名称具有一样名称的成员函数是构造函数。构造函数不能有返回值,甚至不能有return语句。说明一个有返回值的构造函数是错误的,取构造函数的地址也是错误的。
如果一个类有构造函数,在程序中每个该类类型的对象在使用之前由此构造函数进行初始化(有关初始化的更多信息参见本章后面的“用特殊成员函数进行初始化”)。
构造函数是在对象的创建点上被调用的。创建对象可以是:
全局对象(文件范围或外部链接的)。
在一个函数或者小的封闭块中的局部变量。
用new运算符创建的动态对象。new *** 作在程序的堆或自由存储区中分配一个对象。
因显式调用构造函数而创建的临时对象(详见本章后面的“临时对象”)。
因编译器隐含调用构造函数而创建的临时对象(详见本章后面的“临时对象”)。
其它类的数据成员。在创建类类型的对象时,若此类类型由其它类类型变量组成,将会引起该类中每个对象的创建。
一个类的基类子对象。创建派生类类型的对象时会引起基类构件的创建。
构造函数的作用
一个构造函数执行各种任务,但对于程序员来说,这些任务是不可见的,你甚至可以不必为构造函数写任何代码。这些任务都同建立一个完全的、正确的类类型对象实例有关。
在MS C++中(同样也在很多其它C++中)一个构造函数:
初始化对象的虚拟基指针(vbptr)。如果该类是由虚拟基类派生出的,则这一步要执行。
按说明的顺序调用基类和成员的构造函数。
初始化对象的虚拟函数指针(vfptr)。如果该类有或者继承了虚拟函数,则这一步要执行,虚拟函数指针指向类的虚拟函数表(v-table),并且使虚拟函数的调用同代码正确绑定(binding)。
在构造函数体中执行可选的代码。
当构造函数结束以后,所分配的存储器就是一个给定类类型的对象。因为构造函数执行这些步骤,故虚拟函数的“迟后绑定”形态可以在虚拟函数的调用点得以解决,构造函数也要构造基类以及构造组合对象(作为数据成员的对象),迟后绑定是C++实现对象的多态行为的机制。
说明构造函数的规则
构造函数具有同类名相同的名称。只要遵守重载函数的规则(有关详情参见第12章“重载”),可以说明多个构造函数。
语法
类名称(参量说明表opt) cv-修饰符表opt
C++定义了两种类型的构造函数,缺省的和拷贝的构造函数。如表112所述。
表112 缺省的和拷贝构造函数
构造函数的种类 参量 目的
缺省构造函数 可以无参量调用 构造一个类类型的缺省对象
拷贝构造函数 可以接受对同种类类型的引用作为唯一参量 拷贝类类型的对象
缺省构造函数不要参量即可调用,但你可以说明一个带有参量表的缺省构造函数,只要让所有的参量有缺省值即可。同样,拷贝构造函数必须接受同一类类型的引用作为唯一参量。但可以提供更多的参量,只要后续的参量具有缺省值即可。
如果你不提供任何构造函数,编译器会试图生成一个缺省的构造函数。同样,如果你没有提供拷贝构造函数,编译器也会试图产生一个。编译器产生的构造函数视为公有的成员函数。如果你说明一个拷贝构造函数,而其第一个参量是一个对象而不是一个引用,则会产生错误。
编译器生成的缺省构造函数建立对象(初始化vftables和vbtables,如前面所述),并调用基类及成员的缺省构造函数,但不会采取其它的行动。基类和成员的构造函数只要存在,是可访问的,并且是无二义性的就会被调用。
编译器生成的拷贝构造函数建立一个对象,并且采用一个成员方式的拷贝来复制要被拷贝的对象的内容。如果基类或成员的构造函数存在,则它们将被调用,否则,就采取位方式的拷贝。
如果所有的基类和该类类型的成员类具有接受一个常量参量的构造函数,则编译器生成的拷贝构造函数接受一个唯一的参量的类型是const type&;否则,编译器生成的拷贝构造函数接受的唯一参量的类型是type &。
你可以用一个构造函数去初始化一个const和volatile对象,但构造函数本身不能说明为const和volatile的。对于构造函数唯一合法的存储类型inline,对于构造函数使用任何其它的存储类修饰符,包括__declspec关键字都会引起错误。构造函数和析构函数除了__stdcall外不能说明为其它的调用约定。
派生类是不能继承基类中的构造函数的。当一个派生类的对象在创建的时候,它是从基类构件开始构造的,然而才进入派生类构件。编译器使用每个基类的构造函数作为完整对象初始化的一部分(除了虚拟派生有所不同,参见本章后面的“初始化基类”)。
显式地调用构造函数
在程序中,为创建给定类型的对象,可以显式地调用构造函数。例如:创建两个Point型对象的描述一条线段的端点,可以写如下代码:
DrawLine(Point(13,22),Point(87,91);
创建了两个Point对象,传递给DrawLine函数,并在该表达式(函数调用)结束后拆除。
另外一个显式地调用构造函数的情况是在一个初始化中:
Point pt=Point(7,11);
创建了一个Point类型的对象,并用接受两个整形参量的构造函数进行初始化。如前面的两个例子中,通过显式地调用构造函数创建的对象是无名的对象,并在它们所创建的表达式中是有生存期的。在本章后面的“临时对象”中将对这一点进行深入的讨论。 在构造函数内调用成员函数和虚拟函数
在构造函数里面调用任何成员函数通常是很安全的,因为在执行第一行用户代码之前,对象已经完全建立起来了(已经初始化了虚表等等)。但是当成员函数调用了其抽象基类的虚拟成员函数时,在构造函数和析构函数调用此成员函数存在着潜在的不安全性。
构造函数可以调用虚拟函数。在调用虚拟函数时,被调用的是在构造函数自身的类中定义的函数(或者从其基类中继承的函数)。下面的例子显示了一个虚拟函数在一个构造函数中被调用时发生的情况。
#include <iostreamh>
class Base
{
public:
Base();//缺省构造函数
virtual void f(); //虚拟成员函数
};
Base::Base()
{
cout<<"Constructing Base sub-object\n ";
f();//在构造函数中调用虚拟成员函数
}
void Base::f()
{
cout<<"called Base::f()\n";
}
class Derived:public Base
{
public:
Derived();//缺省构造函数
void f(); //该类虚拟函数的实现
};
Derived::Derived()
{
cout<<"constructing Derived object\n";
}
void Derived::f()
{
cout<<"Called Derived::f()\n";
}
void main()
{
Derived d;
}
在上面的程序运行的时候,Derived d的说明会引发下列一系列事件:
1 类Derived的构造函数(Derived::Derived)被调用。
2 在进入到Derived类的构造体之前,基类Base的构造函数被调用。
3 Base::Base调用函数f,它是一个虚拟函数。通常被调用的函数会是Derived::f,因为对象d是Derived类型的对象。但因为Base::Base是一个构造函数,此时的对象是一个Derived类型的对象,故Base::f将会被调用。
构造函数与数组
数组的构造只能使用缺省的构造函数。缺省构造函数要么不接受任何参量,要么对于它的所有参量都有缺省的值。数组通常是按升序来构造的,该数组的每一个成员的初始化都是使用同一构造函数。
构造的次序
对于派生类或其成员数据是类类型的类,构造发生的顺序有助于你理解,在任一给定的构造函数中你能够使用对象的哪一部分。
构造与继承
一个派生类的对象是从基类到派生类通过按次序为每个类调用构造函数来构造的。
每个类的构造函数能仅依赖于被完全构造好的它的基类。
有关初始化的完整描述,包括初始化的顺序,见本章后面的“初始化基类及成员”。
构造与组合类型
含有类类型数据成员的类称为组合类。当创建一个组合类类型的对象时,含有类的构造函数在该类的构造函数之前调用。 有关这种情况的初始化,见本章后面的“初始化基类及成员”。
比如说
class A
{private:
int a,int b;
public:
A(int a1=0,int a2=0)
{a=a1;a=a2;}//这个时候就调用构造函数了,即在向private成员赋值的时候,会调用构造函数
}
复制构造函数的调用有三种情况,第一种是对一个类得对象赋初值的时候
比如说
class A
{private:
int a,int b;
public:
A(class &B)
{a=Ba;b=Ab;}
第二种是当返回一个类的时候
f()
{
return A;
}
第三种是当想要使一个函数的参数为一个类得时候,那么就会调用复制构造函数
如
f(class& A)
{
}
其实一般情况下,复制构造函数是不用写出来的,只需要调用默认的就可以了,除非是代码中已经有了和指针挂钩的东西,这个时候最好就写出来复制构造函数,否则就会造成悬空的指针,程序会崩掉。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)