C++ class notes(二)

constructor && destructor

这两个函数是最基本的成员函数。它们与对象的生成与销毁相关。constructor用于创造对象,destructor用于销毁对象。这两个成员函数没啥好说的,只是简单易懂的概念与语法。

constructor

从时间上讲,只有当constructor完成它的工作时,对象才被创建,constructor用于创造对象。

当声明一个类的对象时,系统自动调用constructor。

default constructor:没有参数的constructor或者所有参数都有默认值的constructor,不需要显示初始化。 一个类只能有一个default constructor, 否则会因其模糊而无法通过编译。

如果你不提供一个default constructor,complier会自动为我们提供一个default constructor,它是空实现,不做任何事情。

归功于函数重载,一个类能有多个constructor,只能有一个default constructor,constructor没有返回值,没有声明类型。

    explicit Stonewt(double);	// conversion constructor
	Stonewt(int stn, double bls);
	Stonewt();		// default constructor
	~Stonewt();
	void sholw_lbs() const;
	operator int() const;	// convert type Stonewt to int 
	operator double() const;	// convert type Stonewt to double

destructor

一个类只能有一个destructor。和constructor一样,它没有返回值,没有声明类型。当constructor中使用new/new [ ] 申请内存时, destructor必须使用delete/ delete[ ] 释放申请的空间。destructor很重要。

conversion constructor

conversion 有两个分支, another type to class type, or class type to  another type.

此处采用another type而不是 built-in type 是因为我们也许会在两种类之间进行类型转换。

conversion constructor是只有一个参数的constructor,它用于将参数的类型转换为类的类型。在该成员函数的声明部分,我们常使用explicit 关键字表示显示调用,以免不必要的麻烦。explicit修饰符的使用,类似friend关键字,出现在头文件的声明部分。而在函数的具体定义处,它并不出现。

而在test file中,可以这样写

Stonewt mycat;
mycat = Stonewt(1.32);  //ok
mycat = (Stonewt) 3.14;   // ok

 再来看将 converse class type to another type。注意它在头文件中的形式,注意它返回了对应类型的数值。其实就是运算符重载,和 operator<<没有太大的区别。

Stonewt::operator int() const
{
	return int(pounds + 0.5);
}

Stonewt::operator double() const
{
	return pounds;
}

以下是test file中的代码片段,我们可以这样使用该函数。就像对基本内置类型使用类型转换一样。

Stonewt obj1();
int rt = (int) obj1;  // valid
int rtt = int (obj1);    //valid

copy constructor && assignment operator

两者所做的事情很大程度上有重叠。在具体的实现上也容易混淆。

当类的private部分存在用以标识内存块的指针时,构造函数constructor用new 动态申请内存。

此时,copy constructor 和 operator=不能依赖complier 为我们生成,需要自己写,在函数中实现 deep copying,即对指针所指向的内容赋值,而非仅仅赋值指针的地址,以免当对象销毁时多次释放已经释放的内存。

copy constructor

copy constructor用于将一个对象赋值给一个新创建的对象。换言之,copy constructor用于初始化,包括在函数参数中传递值与返回值。

Class_name(const Class_name& );  // a general prototype for a copy constructor

当一个新对象被创建并且被初始化为一个已经存在的同类型对象时,copy constructor被使用。

较为明显的情况是将一个对象初始化为一个已经存在的对象

String ditto(motto);    // given that motto is an existing object
String* pstring = new string(motto);

较不明显的情况是当一个程序生成一个对象的拷贝。这包括函数返回一个对象,函数接收对象的值传递,或者需要生成临时对象三种情况。因为传递值意味着创建原对象内容的拷贝。临时对象在不同编译器间情况不同,但当对象传递值时,它们都使用copy constructor。

给几个例子

return_type class_name::function_name (argument_list); // a method funciton prototype

// say , String is class name
String& String::add(const String& ) ; // pass-by-reference, return reference
String String::add(const String& ) ; // pass-by-reference, return value
String String::exchange(String ); //   pass-by-value, return value

 以上三个函数调用的都会使用copy constructor。三个函数都传递了值,需要对原有对象内容的进行拷贝。

所以,这里有一个结论:尽量传递引用,而非值。

一方面是为了效率,避免调用copy constructor, delete constructor,降低程序效率。

第二个原因是基类的指针或引用可以调用派生类的功能。(这个后面再细讲)。

assignment operator

assignment operator处理的是将一个对象赋值给同类型的另一个对象,处理的是赋值的情况。

注意赋值与初始化不同。当一个语句创建新对象时,它使用初始化;如果一个语句改变已存在对象的值,它就是赋值了。

String sirius;
String alpha = sirius; // initialization

String dogstar;
dogstar = sirius; // assignment

 对于基本内置类型,对自我的赋值是正常的操作,但对类,需要在具体实现中考虑自我赋值的情况。当类的private成员中有指针变量时,assignment operator函数中通常需要 使用 delete [ ] 释放原有指针指向的空间,再重新申请一块新内容。

此处给出一个参考例子,并对operator可能的各种错误情况给出假设。  

class String
{
private:
    char* name;
    int length;
publlic:
    String();
    String(const String&);
    String& operator=(const String&);
}


String::String()
{
    name = new char[4];
    strcpy(name,"C++");
    length = 3;
}

String& Stirng::operator=(const String& st)
{
    if(st == *this)
        return *this;
    delete[] name;
    length = st.length;
    name = new char[length + 1];
    strcpy(name, st.name);
    return *this;
}

// other implementaiton

String obj1,obj2;  // two object, both have a pointer variable

obj1 = obj2;  // assignment operator

在见识了正确的书写示例后,再来看看对错误情况的分析。

此处给出几种错误的假设。 

String sirius;
String alpha = sirius; // initialization

String dogstar;
dogstar = sirius; // assignment

第一种。我们可以不使用delete[] 语句删除先前指向的内容。直接改变指针的指向,但这样这块内容就无法再追踪了,它申请了,不再使用了,又不释放,且无法重新分配,会造成内存泄漏。

第二种。此外,对象obj1的指针指向了对象obj2的指针变量指向的内容。那么在两个对象结束时,destructor会释放已经释放的指针所指向的内存,这是一种编译错误。

第三,不书写delete[ ]语句,重新申请一块内存,并拷贝obj2的指针所指向的内容给obj1的指针。同样的,和第一种假设一样,直接失去了对obj1指针先前申请内存的追踪,造成内存泄漏。

所以,对于operator= ,我们需要self-assignment check, 当不是self -assignment时,需要先释放,再申请内存。

after comparison

单从结论上记忆的话,在assigement operator中,需要考虑self-assignment 的情况。且需要使用delete[ ]释放空间。而copy constructor因为用于初始化对象,对象本身的指针并未申请内存块,所以不需要且不能使用delete [ ] 语句。

other special member function

其他的special member funcitons还有address operator, 即operator&。它返回调用对象的地址,即this的值。这个没啥好讲的,它所做的,编译器为我们生成的,就是我们需要的。至少目前,我还没遇到需要特殊处理它的情况。

C++11 还有move constructor 和 assignment constructor,留待后续再讲。

In Summary

当我们学习类的知识时,学习它的特殊成员函数很有必要。它们是新的语法,也是工具。在掌握它们之后,我们会更亲近类,更了解类。随着使用,了解会越来越深,理解也会更到位。

  • 34
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值