构造函数的杂感(一)

不需显式地调用,在创建类对象时,编译器自动调用的函数.构造函数和析构函数就是这种函数.

构造函数特点:  1、函数名与类名相同;

              2、无返回值。

class C{

public:

       C() { name = “Unknown”;}

       C( string& n ) { name = n; }

private:

       string name;

}

int main() {

       C c1;                           // name = “Unknown”

       C c2( “Jack” );           // name = “Jack”

       return 0;

}

看到没?这便是构造函数的神奇之处!!

 

下面分别剖析知识点:

 

 

一、构造函数可以约束对象创建。

比如说有这种情况:如果你想创建一个对象,那你必须同时给出它的编号,否则连门都没有!!见下例:

class Book {

public:

       Book( unsigned ID ) { id = ID; }

private:

       Book();

       unsigned id;

};

void main() {

       Book a;                      // ERROR !!不给出编号别想成功创建!

       Book b(152465771);  // OK! 可以.

}

其实,即使把“ Book(); 删掉, a  照样定义不成。因为 Book 类没有提供公有的默认构造函数。

多数情况下,编译器会为类生成一个公有的默认构造函数,只有以下两种情况除外:

一、一个类显式声明了任何构造函数(不管是不是默认函,只要是构造函数),编译器不生成默认构造函数!这时候,想要默认函,就少玩会儿《World of Warcraft》,自己动手鼓捣一个!!

二、一个类声明了一个非公有的默认构造函数,编译器不生成默认构造函数。

 

回归本例,因为Book 类显式声明一个构造函数了,所以编译器便不再自作主张生成个默认的了。于是呢, a  就郁闷了,咋会儿事呢?我到底哪里做错了呢?Book 类,还有main 函数,你们有没有考虑我的感受……

 

二、拷贝构造函数

class Person {

public:

       height

       weight

       age

       address

       hobby

       ...

       Person( Person& );

};

void main() {

       Person Jack;

       Person Tom( Jack );   //这便是是拷贝构造函数,经这么一搞,     

}                                      //Tom的一切个人资料

 

构造函数有两种原型:

       Person( Person& );

   Person( const Person& );

两种原型都是引用,因此  Person( Person );  是错误的!

可以有多于一个参数,但第一个以后的所有参数都必须有默认值:

       Person( const Person& p, bool married = false );

什么时候应该为类设计拷贝构造函数呢?

答:当一个类包含指向动态存储空间指针的数据成员,这时候就很有必要为该类设计一个拷贝构造函数了。因为编译器生成的拷贝构造函数会使 Tom Jack 指向相同的某个存储空间,这样任何一方资料的改动都会导致另一方的也莫名其妙地改了,“死都不知道怎么死的”,这是非常危险的!!

正确的做法是自己动手设计一个拷贝构造函数,使Tom 指向另一块儿存储空间,其空间里的内容和 Jack 的一模一样。见下例:

使用编译器生成的拷贝函数版本:

 

#include<iostream>

#include<string>

using namespace std;

 

class Namelist {

public:

       Namelist() { size = 0; p = 0; }

       Namelist( const string [], int );

       void set( const string& , int );

       void set( const char* , int );

       void dump() const;

private:

       int size;

       string* p;

};

Namelist::Namelist( const string s[], int si ) {

       p = new string[ size = si ];

       for ( int i = 0; i < size; i++ )

              p[ i ] = s[ i ];

}

void Namelist::set(const string& s, int i ) {

       p[ i ] = s;

}

void Namelist::set( const char* s, int i ) {

       p[ i ] = s;

}

void Namelist::dump() const {

       for ( int i = 0; i < size; i++ )

              cout << p[ i ] << '/t';

       cout << endl;

}

 

int main() {

       string list[] = { "Lab", "Husky", "Collie" };

       Namelist d1( list, 3 );

       d1.dump();

       Namelist d2( d1 );

       d2.dump();

       d2.set( "Great Dane", 1 );

       d2.dump();

       d1.dump();

       return 0;

}

输出为:

Lab        Husky     Collie

Lab        Husky     Collie

Lab        Great Dane  Collie

Lab        Great Dane  Collie

用两张图片来解释原因吧:

图片

 

图片

 

 

这便是为什么 d1的数据被莫名其妙地改了! 这便是《谁动了我的奶酪》!!

那么且看正确的方法——

自定义拷贝构造函数版本:

#include<iostream>

#include<string>

using namespace std;

 

class Namelist {

public:

       Namelist() { size = 0; p = 0; }

       Namelist( const string [], int );

       Namelist( const Namelist& );

       void set( const string& , int );

       void set( const char* , int );

       void dump() const;

private:

       int size;

       string* p;

       void copyIntop( const Namelist& );

};

 

Namelist::Namelist( const string s[], int si ) {

       p = new string[ size = si ];

       for ( int i = 0; i < size; i++ )

              p[ i ] = s[ i ];

}

Namelist::Namelist( const Namelist& d ) {

       p = 0;

       copyIntop( d );

}

void Namelist::set( const string& s, int i ) {

       p[ i ] = s;

}

void Namelist::set( const char* s, int i ) {

       p[ i ] = s;

}

void Namelist::dump() const {

       for ( int i = 0; i < size; i++ )

              cout << p[ i ] << '/t';

       cout << endl;

}

void Namelist::copyIntop( const Namelist& d ) {

       delete[] p;

       if( d.p != 0 ) {

              p = new string[ size = d.size ];

              for ( int i = 0; i < size; i++ )

                     p[ i ] = d.p[ i ];

       }

       else {

              p = 0;

              size = 0;

       }

}

 

int main() {

       string list[] = { "Jack", "Tom", "Lily" };

       Namelist d1( list, 3 );

       d1.dump();

       Namelist d2( d1 );

       d2.dump();

       d2.set( "Armstrong", 1 );

       d2.dump();

       d1.dump();

       return 0;

}

输出结果是:

Jack    Tom     Lily

Jack    Tom     Lily

Jack    Armstrong     Lily

Jack    Tom     Lily

原理:

图片

 

很明显,对 d2 的修改不会再牵制到 d1 的任何数据了,智慧的古巴比伦法老制定了《十二铜表法》,其中最著名的一条便是“私有财产,神圣不可侵犯”。天知道,这句话对后来的世界发展是多么的至关重要!!社会主义不敢说,反正我就知道几百年来任何模式、形状和程度的资本主义制度都是在这句话上繁衍而来的。可喜的是,在本程序中,也看到了这一点。

 

 

三、转型构造函数

用于类型转换,只有一个参数。

#include<iostream>

using namespace std;

 

class Person {

public:

       Person() { name = "Unkown"; }

       Person(  const char* n ) { name = n; }

private:

       string name;

};

 

int main() {

       const char* p = "souphy";

       Person S( p );

 

       return 0;

}

看到没?本来私有数据成员 name 是个吊 string 类型的。可参数明明是个 const char 类型的指针,只所以能够创建成功,原因就在于  Person( const char*  n ) { name = n; } 在背地里给它使着劲呢!

         图片

      

               你懂了吗?!!!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值