c++学习笔记(7.构造与析构)

原创 2013年12月05日 00:43:20

本节知识点:

1.c++中的构造函数:

   a.c++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数。构造函数在定义的时候可以有参数,但是没有任何返回类型的声明。
   b.在c++编译器中,在类定义对象的时候就会顺便自动调用这个对象的构造函数,有时候也需要手动调用,代码如下:
#include <stdio.h>

class list
{
private:
	int a;
	int b;
	int c;
public:
	list(int v)
	{
		a = b = c = v;
	}
	void list_printf()
	{
		printf("%d %d %d \n",a,b,c);
	}
};
int main()
{ 
	list l(5); //自动调用方式一
	list d = 6; //自动调用方式二
	list p = list(7); //手动调用
	l.list_printf();
	d.list_printf();
	p.list_printf();
	
	list ar[3] = {1,2,3}; //对于类数组 只能这样手动调用了 
	list ar1[3] = {(1),(2),(3)};
	list ar2[3] = {list(1),list(2),list(3)};
	 
	return 0;
}

注意:上面定义类的三种方式
   c.类的成员函数和普通函数一样都可以进行重载,也就是说构造函数支持重载,代码如下:
#include <stdio.h>

class list
{
private:
	int a;
	int b;
	int c;
public:
	list()
	{
		a = 1;
		b = 2;
		c = 3;
		printf("list\n");
	} 
	list(int v)
	{
		a = b = c = v;
	}
	void list_printf()
	{
		printf("%d %d %d \n",a,b,c);
	}
};
int main()
{ 
	list a = list(); //无参数的构造函数的调用 
	list a1;  //与上面语句等效  切记list a1(); 是错误的~~~ 
	 
	list l(5); //自动调用方式一
	list d = 6; //自动调用方式二
	list p = list(7); //手动调用
	l.list_printf();
	d.list_printf();
	p.list_printf();
	
	list ar[3] = {1,2,3}; //对于类数组 只能这样手动调用了 
	list ar1[3] = {(1),(2),(3)};
	list ar2[3] = {list(1),list(2),list(3)};
	 
	return 0;
}

   d.两个特殊的构造函数:
       无参构造函数:当类中没有定义构造函数时,编译器会默认提供一个无参构造函数,并且其函数体为空。
其目的主要是为了,兼容那些没有定义构造函数的类的定义(因为我们知道定义类的时候,编译器会自动调用构造函数的),没有太多的实用价值。但是注意:第一可以自己定义无参构造函数,来代替编译器提供的那个函数,此时函数体就不再是空函数了。第二,没有构造函数的类的定义方式list a; 或者 list a = list();  切记list a(); 是错误的方式!!!
       拷贝构造函数:拷贝构造函数其实是一个参数为自己类的引用的构造函数,如:list(const list& l);   当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值的复制(默认的这个函数的功能是 list a = b;  把b类中的成员变量的值复制给a类中的成员变量,注意a b必须是同一个类)。但是你也可以自己定义拷贝构造函数,此时的函数功能就由你自己定了!!!
示例代码:
#include <stdio.h>

class list
{
private:
	int a;
	int b;
	int c;
public:
	list()
	{
		a = 1;
		b = 2;
		c = 3;
		printf("list\n");
	} 
	list(int z)
	{
		a = b = c = z;
	}
	void list_printf()
	{
		printf("%d %d %d \n",a,b,c);
	}
	list(const list& r)   //可以屏蔽这个函数 看默认拷贝构造函数的功能 
	{
		printf("copy hello\n");
	}
};
int main()
{ 
	list a = list(); //无参数的构造函数的调用 
	list a1;  //与上面语句等效  切记list a1(); 是错误的~~~ 
	list p(6);
	list q = p;
	q.list_printf(); 
	return 0;
}

注意:1.当类中没有定义任何一个构造函数,c++编译器会提供无参构造函数和拷贝构造函数
            2.当类中定义了任意的非拷贝构造函数时,c++编译器就不会提供无参构造函数
            3.这里说下无参构造函数、有参构造函数、拷贝构造函数,他们的关系是重载的关系,在定义一个类的时候只能自动调用他们三者中的一个构造函数,选择方
式是根据定义方式来说的。

2.c++类中的初始化列表:

   a.在类中的成员变量定义的时候,有些成员变量是需要赋初值的。比如说:const属性的只读变量引用只有有参构造函数的类。当存在这些不赋初值就报错的情况,而且类中的成员变量不能直接赋初值的时候,就需要用到初始化列表了。
   b.初始化列表是写在构造函数后面,执行在构造函数前面,对类的成员变量进行初始化的语句,代码如下:
#include <stdio.h>

class M
{
private:
    int mI;
public:
    M(int i)
    {
        printf("M(int i), i = %d\n", i);
        mI = i;
    }
    
    int getI()
    {
        return mI;
    }
};

class Test
{
private:
    const int c;
    M m1;
    M m2;
public:
    Test() : c(1), m2(3), m1(2) //初始化列表中成员变量一个都不能少 
    {
        printf("Test()\n");
    }
    Test(int z) : c(10), m2(30), m1(20)
    {
		printf("Test int\n");
    }
    void print()
    {
        printf("c = %d, m1.mI = %d, m2.mI = %d\n", c, m1.getI(), m2.getI());
    }
};


int main()
{
    Test t1(2);
    t1.print();

    getchar();
    
    return 0;
}
针对上面的代码要注意几点:
         第一:初始化列表是写在各个构造函数的后面的,切记成员变量一个都不能少否则编译出错,初始化列表是在构造函数执行之前执行的,成员变量的初始化顺序与在类中声明的顺序有关,与在初始化列表中的顺序无关。
         第二:这个问题很重要,是关于c++中类的对象的赋值的问题,对于一个已经初始化完成的类的对象Test t1;  可以进行t1 = 10;的操作,此时调用t1的Test(int a)构造函数,但是要注意的是,如果有初始化列表,虽然t1的成员变量的值发生了变化,但是这个过程不再是初始化了,而是直接对t1的成员变量根据初始化列表中的值进行赋值操作。可以用const int c;这个只读变量来判断,如果这个过程是初始化,c这个成员变量是可以编译通过的,但是这个过程是赋值过程,c又是const属性,所以编译会出错!!! 同时对于t1还可以进行t1 = t2;的操作,这个过程即没有调用有参构造函数,也没有调用拷贝构造函数。是简简单单的把t2的成员变量的值,赋值给t1的成员变量中去!!!但是如果Test t1是一个函数的形参void fun(Test t1),当调用这个函数的时候 fun(t2);,t2当做实参,此时调用了拷贝构造函数!!!
         第三:类中的const成员变量时肯定会被分配空间的,只是一个只读变量,因为编译器无法直接得到const成员变量的初始值,因此无法进入符号表成为真正的常量。
         第四:初始化与赋值是不同的,这个是在补充第二点,初始化是用已存在的对象或值对正在创建的对象进行初值设置。赋值是用已存在的对象或值对已经存在的对象进行值的设置。

3.c++中的析构函数:

   a.c++中在对象撤销的时候,对象会自动调用一个清理函数,与构造函数相对应,叫析构函数。析构函数为~classname(),且没有参数也没有任何返回值类型。
示例代码:
#include <stdio.h>

class Test
{
private:
    int mI;
public:
    Test(int i) : mI(i)
    {
        printf("Test(), mI = %d\n", mI);
    }
    
    ~Test()
    {
        printf("~Test(), mI = %d\n", mI);
    }
};

void run()
{
    Test t1(1);
    Test t2(2);
}

int main()
{
    run();
    printf("Press any key to continue...");
    getchar();
    
    return 0;
}  
注意:析构函数的调用顺序!!!
通过上面的例子,我们发现析构函数的调用顺序是与构造函数的调用顺序完全相反的!!!那我们总结下构造函数的调用顺序:
第一:当类中有成员变量是其他类的对象时,首先调用成员变量的构造函数。(注:调用顺序与声明顺序相同)
第二:调用自身类的构造函数。

4.构造函数与析构函数的关系:

   第一点:构造函数与析构函数的产生都是因为,他们可以自动调用(省事),构造函数是类创建对象的时候自动调用的,析构函数是对象销毁的时候自动调用的。
   第二点:在某些情况下,c++编译器可以提供默认的构造函数(即无参构造函数和拷贝构造函数),但是析构函数则需要程序员自己手写,编译器提供不了。
   第三点:构造函数与析构函数的调用顺序是完全相反的。

课后练习:

1.可以直接调用构造函数吗?

        在这里多说几句,首先说说这个问题,这个问题的含义是在main函数或者类的成员函数中,可不可以不通过对象直接调用test(); 这个构造函数,结果是可以编译通过。但是没有对象就直接调用对象里面的成员函数,这也太荒唐了!!!所以编译器此时让构造函数得到了一个临时对象,导致这个构造函数完成不了任何功能!!!这里还要声明一个问题,就是通过对象来访问对象的构造函数是编译出错的,原因未知!!!总之这个问题有些无趣,荒谬。给个小例子附在后面(可以不看):
#include <stdio.h>

class Test
{
private:
    int mI;
    int mJ;
    const char* mS;
public:
    Test()
    {
        printf("Test()\n");
        
        mI = 0;
        mJ = 0;
    }
    
    Test(const char* s)
    {
        printf("Test(const char* s)\n");
        
        Test();
        
        mS = s;
    }
    
    ~Test()
    {
        printf("~Test()\n");
    }
    
    void print()
    {
        printf("mI = %d, mJ = %d, mS = %s\n", mI, mJ, mS);
    }
};

void run()
{
    Test t = Test("hello world"); // Test t("hello world");
    
    t.print();
}

int main()
{
    run();
    //Test a;
    //a.Test(); 
    getchar();
    return 0;
}





相关文章推荐

Effective c++学习笔记——条款09:绝不在构造和析构过程中调用virtual函数

Never call virtual functions during construction or destruction     这是本条款的核心,不该在构造函数和析构函数期间调用virtua...

GeekBand C++学习笔记——继承、复合当中的构造与析构

引言        为什么今天要讨论继承、复合当中的构造和析构?对于C++来说,类的构造函数和析构函数的主体通常是只负责自身的构造和析构,而继承和复合之后,往往会调用父类和复合类的构造函数和析构函数,...

C++学习笔记18 继承中的构造与析构

赋值兼容性原则: 子类对象可以当做父类对象使用; 子类对象可以直接赋值给...

Effective C++学习笔记二(构造/析构/赋值运算)

条款05:了解C++默默编写并调用哪些函数 1. 编译器可以暗自为class创建default构造函数,copy构造函数,copy assignment操作符,以及析构函数(如果某个base clas...

c++学习笔记之类和对象、构造析构拷贝构造函数、动态内存友元函数

2017-3-21 ============ 主题:     1.类和对象     2.封裝     3.构造函数与析构函数     4.标准库类型string     5.static...
  • gmq_syy
  • gmq_syy
  • 2017年03月27日 16:53
  • 90

Php面向对象学习笔记 – 构造、析构、对象赋值、克隆

Php面向对象学习笔记 – 构造、析构、对象赋值、克隆   class student {        public  $stu_id;       // 定义成员变量        pu...

【C++】【学习笔记】【015】构造和析构

在创建对象时,系统都会自动调用一个特殊的方法,即构造器。 构造器和通常方法的主要区别: 1、构造器的名字必须和它所在的类的名字一样; 2、系统在创建某个类的实例时会第一时...
  • jay_yin
  • jay_yin
  • 2014年11月10日 13:54
  • 250

【C++】【学习笔记】【017】继承机制中的构造器和析构器

1、基类必须在子类之前初始化。如:对于基类Animal和子类Pig,如果基类有个构造器Animal(),它将在创造Pig类型的对象时最先被调用,如果Pig类也有一个构造器Pig(),它将在第二个被调用...
  • jay_yin
  • jay_yin
  • 2014年11月10日 17:47
  • 301

swift学习笔记(13)- 构造过程、析构过程

构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。通过定义构造器来实现构造过程,这些...

C++学习-构造+析构+拷贝

new一个对象的原理,构造函数的作用,拷贝构造函数的作用,析构函数的作用,类的组合问题...
  • ydpawx
  • ydpawx
  • 2016年09月06日 23:55
  • 474
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:c++学习笔记(7.构造与析构)
举报原因:
原因补充:

(最多只允许输入30个字)