您的位置:首页 > 编程语言 > C语言/C++

C++学习总结——类和对象、构造函数和拷贝构造函数

2014-05-05 19:48 441 查看
一、类和对象
面向对象思想把现实世界中的物体都封装成对象,而类是所有相同类型对象的抽象,是他们的总体描述。同一个类的不同对象有相同的属性和行为,所以在一个类的声明中需要描述这类对象的属性和行为。在C++中,声明一个类的语法格式如下:
class 类名 :( public 基类名)
{
public:
// 公有成员,通常用来定义类的行为,提供接口供外部访问、
protected:
// 保护型成员
private:
// 私有成员,通常用来定义类的属性
};
完成类的名字及继承关系的定义之后,可以在类的主体中描述这个类的属性和行为。将变量引入类的声明之中就成为类的成员变量,这些变量就是对类的属性的描述。除了属性之外,类的另一个重要组成部分就是它的行为。在C++中我们用函数来描述一个行为。同样,我们将函数引入类中成为它的成员函数,用来描述类对象的行为。
在类的主体中,可以使用public,protected,private三个关键字来定义类的属性和行为的访问级别。通常会在public定义类的行为,提供公共的函数接口供外部访问;在protected部分,可以定义遗传给下一代子类的属性和行为;在private部分,可以定义这个类所私有的属性和。受保护的成员不能被类外访问,这一点和私有成员类似;但有一点与私有成员不同,保护成员可以被派生类的成员函数访问。
使用类创建对象的语法格式如下:
类名   对象名;
得到对象后,就可以调用这个类提供的公有成员函数。调用格式如下:
对象名.公有成员函数;
另外,还可以使用对象类型的指针指向该对象,通过指针来访问该对象的成员。例如定义一个Teacher类型的对象MrChen,使用指向MrChen的指针访问其成员:
Teacher MrChen; // 定义一个Teacher类型的对象MrChen
Teacher × pMrChen = NULL; //定义一个Teacher类型的指针
pMrChen = &MrChen;// 将该指针指向MrChen对象
然后就可以利用“->”运算符访问该对象的成员。
二、构造函数
创建一个类对象时,编译器会自动使用构造函数用来初始化该对象。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。构造函数的名字必须与类名相同,以便编译系统能识别它并把它作为构造函数处理。它没有返回值,实际上构造函数返回的应该就是对象本身。
1.不带参数的构造函数
这种构造函数不带参数,在函数体中对数据成员赋初值。这种方式使得每一个对象的数据成员都得到一组相同的值。
2. 带参数的构造函数
在调用不同对象的构造函数时,从外面将不同的数据传递给构造函数,以实现不同的初始化。构造函数的首部一般形式为:
构造函数名 (类型1 形参1,类型2 形参2,...)
因为用户不能调用构造函数,因此只能在定义对象时给定实参,定义对象的一般形式为:
类名   对象名(实参1,实参2,...);
3. 使用默认参数的构造函数
构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使用默认值。一般在声明构造函数时指定默认参数,在定义时不指定默认参数。
使用带有默认参数的函数时有几点要注意:
a)实参与形参的结合是按从左至右的顺序进行的。因此指定默认值的参数必须放在形参表列中的最右端,否则出错。
b)如果函数的定义在函数调用之前,则应在函数定义中给出默认值。如果函数的定义在函数调用之后,则在函数调用之前需要有函数声明,此时必须在函数声明中给出默认值,在函数定义时不需给出默认值。
三、构造函数的重载
在一个类中可以定义多个构造函数,以便为对象提供不同的初始化的方法。这些构造函数具有相同的名字,而参数的个数或类型不同。这称为构造函数的重载。
编译系统根据建立对象时给出的参数去确定调用哪一个构造函数。若建立对象时没有给出参数,系统调用无参构造函数。
在建立对象时不必给出实参的构造函数,称为默认构造函数。显然,无参构造函数属于默认构造函数。一个类只能有一个默认构造函数,也就是说可以不用参数而调用的构造函数,一个类只能有一个。在一个类中定义了全部是默认参数的构造函数时,不能再定义重载构造函数,因为编译系统无法识别应该调用哪个构造函数。
四、拷贝构造函数
拷贝构造函数也是构造函数,但它只有一个参数,这个参数是本类的对象,而且采用对象的引用的形式(一般约定加const声明,使参数值不能改变,以免调用此函数时不慎使实参对象被修改)。拷贝构造函数的作用就是将实参对象的各成员值一一赋值给新对象中对应的成员。 例子:
class Node {
public:
Node() ;
Node(int id , string name , double x , double y , int addrType = 0 ) ;
Node(const Node & copyObj) ;

Node & operator=(const Node & copyObj) ;

private:
int _id ; //便于程序中查找节点的id号
string _name ; //节点名字,可作为nam显示节点名
int _addr_type ; //节点地址结构,取值0和1
//0:平坦型,1:分层型
double _xPos ;
double _yPos ;
//标示坐标位置
};

//无参构造函数
Node::Node(){
_id = 0;
_name = "0";
_addr_type = 0;
_xPos = 0;
_yPos = 0;
}
//有部分默认值的构造函数
Node::Node(int id,string name,double x,double y,int addrType ){
_id = id;
_name = name;
_addr_type = addrType;
_xPos = x;
_yPos = y;
}
//拷贝构造函数
Node::Node(const Node & copyObj){
_id = copyObj._id;
_name = copyObj._name;
_addr_type = copyObj._addr_type;
_xPos = copyObj._xPos;
_yPos = copyObj._yPos;
}
//赋值操作符重载
Node& Node::operator=(const Node & copyObj){
_id = copyObj._id;
_name = copyObj._name;
_addr_type = copyObj._addr_type;
_xPos = copyObj._xPos;
_yPos = copyObj._yPos;
return *this;
}
建立对象时使用Node node2(node1);就可以建立一个新对象node2,并将node1对象中各数据成员的值赋给node2中的各数据成员。
       关于赋值操作符“=”重载:它的缺省操作是将相应的乘员变量的值复制,旧的值被自然丢弃。如果对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放;指针的值被复制了,但指针所指内容并未复制。因此,包含动态分配成员的类除提供拷贝构造函数外,还应该考虑重载"="赋值操作符号。 
五、派生类调用基类的构造函数、拷贝构造函数通过派生类的构造函数调用基类的构造函数有两种方式:隐式和显式。隐式方式就是在派生类的构造函数中不指明对应基类的构造函数,这时调用的是基类的默认构造函数(全部参数有默认值或者无参数的构造函数)。显式方式就是在构造函数中指定要调用的基类的构造函数,并将派生类构造函数的部分参数值传递给基类构造函数。一般形式为:派生类构造函数名(总参列表,包括形参类型和形参名):基类构造函数名(参数表,只有形参名,没有形参类型){ 派生类中新增数据成员初始化语句}例子:class TermNode : public Node {public:TermNode();TermNode(int id , string  name , double x , double y , int addrType = 0);TermNode(const TermNode & copyObj) ;TermNode & operator = (const TermNode & copyObj);private:map<int , int> _port2traffic ;//(端口号,对应的业务)vector<unsigned short> _availPortVec ;//可选端口号,该端口相应于传输层与应用层上的port端口};//隐式调用基类无参构造函数TermNode::TermNode(){//将终端节点的可选端口号初始化为0——65535for(vector<unsigned short>::size_type i =0;i!=65535;i++){_availPortVec.push_back(i);}_availPortVec.push_back(65535);}//显式调用基类有参构造函数TermNode::TermNode(int id , string name , double x , double y, int addrType):	Node(id , name , x , y , addrType){for(vector<unsigned short>::size_type i =0;i!=65535;i++){_availPortVec.push_back(i);}_availPortVec.push_back(65535);}派生类调用基类的拷贝构造函数也是类似的形式。我们发现在派生类的拷贝构造函数中的初始化列表中,基类拷贝构造函数的参数是派生类,但是这样子是没有关系的:TermNode::TermNode(const TermNode ©Obj):Node(copyObj) {_port2traffic = copyObj._port2traffic;_availPortVec = copyObj._availPortVec;}//赋值操作符重载TermNode& TermNode::operator = (const TermNode & copyObj){Node::operator = (copyObj);_port2traffic = copyObj._port2traffic;_availPortVec = copyObj._availPortVec;return *this;}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐