精华内容
下载资源
问答
  • 一个类可包含构造函数的个数
    千次阅读
    2019-03-27 23:36:16

    写在最前

      最近在用淘宝上买的《未来教育》模拟考试软件刷C++二级的真题,遇到有一条颇有争议的题目,根据C++标准,题目的解析是有问题的,下面来分析一下。

    原题:
      有如下类定义:
        class MyClass{
          int value;
        public:
          MyClass(int n):value(n) { }
          int getValue() const { return value;}
        };
      则类MyClass的构造函数的个数是()。
      A.1个
      B.2个
      C.3个
      D.4个
      【未来教育解析】本题考查类的默认构造函数和自定义的构造函数。本题中MyClass(int n):value(n){ }为自定义的构造函数,加上默认构造函数,一共有2个构造函数。故本题答案为B选项。

      【个人解析】根据C++的标准,如果程序已定义构造函数,默认情况下编译器就不再隐含生成默认构造函数。如果此时依然希望编译器隐含生成默认构造函数,可以使用“=default”。如:
      class MyClass{
        int value;
      public:
        MyClass() = default;
        MyClass(int n):value(n) { }
      };
      此时类MyClass一共有三个构造函数:自定义的构造函数和编译器隐含生成的默认构造函数和默认复制构造函数。

      未来教育给的答案虽然是正确的,但是解析是错误的。原题的正确解析应该是:有两个构造函数,自定义构造函数和默认复制构造函数

    更多相关内容
  • 的几默认构造函数

    千次阅读 2021-04-18 02:00:49
    构造函数的特性——(1)没有返回值类型(2)在创建对象时由编译器自动调用,且在对象的整个生命周期只被调用次。(3)构造函数可以重载。(4)在构造函数被调用完毕后对象才被创建完成。(5)构造函数必须用public修饰,...

    1.构造函数

    什么是构造函数——构造函数即用来给对象初始化的函数。函数名字与类名相同。

    构造函数的特性——(1)没有返回值类型(2)在创建对象时由编译器自动调用,且在对象的整个生命周期只被调用一次。(3)构造函数可以重载。(4)在构造函数被调用完毕后对象才被创建完成。(5)构造函数必须用public修饰,否则无法创建对象

    构造函数的初始化列表——(1)对象的各数据成员在初始化列表中才是真正的初始化,在函数体中进行的叫赋值。(2)数据成员中必须确定进行初始化的数据都必须放在初始化列中进行初始化。比如const类型数据,引用类型数据,其他类类型成员,且类的构造函数是非缺省的。(关于最后一个例子,我认为的原因是,如果包含的类显示定义了非缺省构造函数,那么就会导致在初始化这个类的对象时无法调用默认构造函数,因此必须对它进行初始化,而不能像T a那样调用默认构造函数然后在构造函数体中进行赋值)(3)初始化列表变量初始化的顺序不是按照列表中的顺序来,而是根据数据成员在类中的声明顺序来进行初始化。(4)在定义构造函数时不写初始化列表不代表就没有初始化列表,编译器会自动生成一个初始化列表,只不过会用随机值对数据成员进行初始化。这点也说明了为什么有些类型数据成员必须显示的在初始化列表中进行初始化。

    默认构造函数——(1)如果在类中没有声明构造函数,则编译器会自动生成一个无参默认构造函数。但如果程序员给出了任何一种构造函数,编译器都不会再默认生成。(2)无参构造函数和全缺省构造函数都称为默认构造函数,且两者不能共存,并不能形成重载。(3)虽然C++语法规定编译器需要给没有显示定义构造函数的类创建一个默认构造函数,但是如果类的数据成员只是基础数据类型,则编译器不会给类生成默认的构造函数,因为即便生成默认的构造函数,也是在初始化列表中初始化随机值,但是如果数据成员中有别的类类型成员,且那个类中显示定义了缺省构造函数,编译器才会给本类生成默认的构造函数,如果在另一个的类中显示定义了非全缺省的构造函数,则编译器会报错“无法引用构造函数”,如果另一个类也没有显示定义构造函数,则本类同样不会自动生成默认构造。(4)编译器什么时候一定会生成默认构造:1.数据成员有别的类类型且显示定义了默认构造函数。2.继承位置  3.虚拟继承  4.类中有虚函数。

    构造函数不能被哪些关键词修饰——(1)const(2)static(3)virtual

    2.析构函数

    什么是析构函数——析构函数用来清理对象资源,在对象被销毁时被自动调用。

    析构函数的特性——(1)名字与类名字相同,在前面加一个“~”,没有返回值类型。(2)不可以重载(3)没有参数。(4)被编译器自动调用,一般不通过对象显示调用。(5)类中没有涉及到资源管理时,析构函数不需要显示给出,涉及到资源管理时,需要程序员显示给出析构函数进行资源回收。

    析构函数什么时候被调用——(1)如果在静态存储区,则在程序结束时被调用。(2)如果创建的是自动存储类对象,在程序执行完对应的代码块后调用。(3)如果是用new创建的对象,则在使用delete时调用。(4)程序有时会创建临时对象来执行特定的操作,例如返回值时。那么会在程序结束该对象的使用时调用。

    默认析构函数——没有显示定义析构函数时编译器会默认给出析构函数。

    3.拷贝构造函数

    什么是拷贝构造函数——创建对象时可以用其他的对象来初始化这个对象,拷贝构造函数的作用就是通过另一个同类对象来初始化对象。

    拷贝构造函数的特性——(1)没有返回值类型(2)参数为const类类型&,不能传值,因为如果传值,则在调用拷贝构造时,会在栈上创建临时对象来保存实参对象,此时又会调用拷贝构造来对临时对象进行初始化,此时就会造成无限递归。(3)可以算作构造函数的一种重载。

    默认拷贝构造函数——(1)同构造函数相似,对于数据成员只有内置类型的类,在没有显示定义构造函数的情况下,编译器不会自动生成默认拷贝构造函数,而是直接选择进行赋值,原因在于免除函数调用的开销。但是如果数据成员中包含其他类类型,在其他类类型成员的类中如果显示定义了拷贝构造函数,则会生成默认的拷贝构造去调用其他类的拷贝构造函数,如果其他类没有显示定义拷贝构造,则两个类都不会生成默认构造函数,都直接采用汇编中的mov。

    浅拷贝与深拷贝——由编译器默认生成的拷贝构造函数(或者说没有生成)执行的是浅拷贝,即只是将对象所在的内存中的内容拷贝到另一个对象内存空间中,如果被拷贝的对象内存空间中保存的是一个指针类型数据,那么钱拷贝的结果只是拷贝了这个地址,结果就是导致两个对象使用了同一份指针指向的资源,在释放资源时就会导致同一个指针被释放了两次,且其中一个对象修改了资源也会导致另一个对象的资源改变。因此在有资源管理的情况下,应当显示定义拷贝构造函数并进行深拷贝。

    写时拷贝——浅拷贝+计数。计数会计算当前使用一个资源的对象的个数,不同的对象只保存该资源的地址,在对象释放资源时,对计算减1,如果结果还大于0,则不释放资源,如果等于0,则代表已经没有其他对象使用这个资源,可以进行释放。在修改资源时,进行深拷贝,使用另一份资源,保证不会修改其它对象的资源。

    4.赋值运算符重载函数

    什么是赋值运算符重载函数——两个已经创建的对象,可以进行赋值操作。重新定义赋值运算符的操作,使赋值运算符作用于这个类的对象时进行程序员想要的操作。

    默认的赋值运算符重载函数——与默认构造函数类似,如果类中数据成员没有其他类成员,则编译器不会生成默认此函数,直接使用mov,在有其他类类型成员的情况下,如果其他类中没有显示定义赋值运算符重载函数,则此类同样不会默认生成此函数,其他类中显示定义了赋值运算符重载函数,此类才会生成默认的此函数。

    赋值运算符重载函数的实现——通常的格式是,T& operator=(T& 形参名),(1)为什么只有一个形参,因为有一个隐藏的this指针不需要程序员显示声明。(2)为什么形参类型要用&,避免值拷贝,效率更高。(3)为什么要有返回值,为了可以进行连续赋值。(4)为什么返回值类型要用引用且通常return *this,为了符合连续赋值的规则,即假设int a,b,c,a=b=c,赋值的规则是将c的值赋值给b,再用b的值赋值给a。b=c相当于b.operator=(c),因此函数必须返回*this才是返回b本身。

    前置++/--,后置++/--的运算符重载——前置++/--直接返回对象本身(返回值类型为引用)即可。后置则要进行值拷贝,因为函数实现需要先创建一个局部对象保存原对象的值,然后返回给调用的对象,由于局部对象在函数结束后会被回收,因此不能返回引用,在返回临时对象的值以后,再对原对象进行++操作即可实现后置++的特性。后置++/--会在函数形参列表加一个"int"以示与前置的区别。T operator++/--(int)。虽然前置的原型为T& operator++/--(),但是返回值无法作为函数重载的特征。

    哪些运算符不能被进行运算符重载——.    .*    :?    ::    sizeof。

    运算符重载要注意什么——(1)不能重载不存在的运算符。(2)在定义函数时要使其结果与运算符意义一致,不能重载+,而实现-运算。

    5.小结(本文许多说法不一定正确,是我自己的总结)

    (1)为什么C++类会生成这6种默认成员函数,我认为是为了使自定义类型的使用和内置类型的使用相似。 C++的目标是使得使用类与使用基本的内置类型(如char,int)尽可能相同。

    (2)小结一下构造函数,拷贝构造,赋值运算符重载。这三者总的来说其实就是致力于修改对象的内存空间,所以为什么编译器有时会自动生成它们,有时又不会,总的思想就是,我现在要修改一个对象的内存空间,如果你没有给我修改的方案,即显示定义对应的函数,那么我就直接按照自己的方法进行操作就好,没必要再创建一个函数,再把我操作的方法写入函数中,再调用这个函数。但是如果你给了我修改的方案,那么我就得调用你给的函数,为了调用你给的函数,我必须自动生成对应的函数来调动你的函数。

    展开全文
  • 【C++】构造函数 利用构造函数对象进行初始化

    千次阅读 多人点赞 2022-01-25 16:01:26
    有关构造函数与析构函数的知识点

    7个月之后的补充: 说真的,别再收藏这篇文章了,写的真的很拉跨,建议学习并收藏C++ 六个默认成员函数 + this指针_CPP的底层是哲学的博客-CSDN博客

    也是我写的,质量会好很多!!!!!! 

    前言:

    因为类对象的数据成员的值是很重要的 而对类对象数据成员初始化这一重要而频繁的操作 当然越简便越好 所以这里讲的就是对对象数据成员初始化的一些方法

    9.1.1 对象的初始化

    1. 在声明类时直接对数据成员进行初始化 如下所示

    在某红书中说到 在声明类时对数据成员初始化是不对的 但是在 c++11标准下 是允许在声明类时 对数据成员直接赋初值的 因为这本书比较落后了 所以内容没有更新 在vs2019中 这种方法是没有报错的 但是注意考试的时候要填不能在声明类时直接对数据成员赋初值

    2. 把数据成员声明为公用成员

    把数据成员声明为公用成员后 在定义对象时 可以像定义结构体变量那样对数据成员初始化 但是绝大多数情况下 数据成员都不会声明为公用的 所以这种方法并不可取

    9.1.2 用构造函数实现数据成员的初始化

    c++提供了构造函数来处理对象的初始化

    ①  用户不能调用构造函数,在建立对象时自动执行,且只能执行一次。一般声明为public

    ②  构造函数是在声明类的时候由类的设计者定义的,程序用户只须在定义对象的同时指定数据成员的初值即可。

    ③  构造函数名必须和类名同名,不能随意命名。

    ④  构造函数不具有任何类型,不具有返回值。 可以有参数。

    ⑤  可以用一个类对象初始化另一个类对象 如 Time t1;  Time t2 = t1;  这样就把t1的各数据成员的值复制到t2相应的各成员 而不调用t2的构造函数Time();

    ⑥  构造函数的函数体中还可以包含如cout的其他语句 但是不推荐这样做

    ⑦  如果类的定义者没有自己定义构造函数,则c++系统会自动形成一个构造函数,这个构造函数函数体是空的,没有参数,不执行任何操作。

    class Time
    {
    public:
    	Time()
    	{
    		hour = 0;
    		minute = 0;
    		sec = 0;
    	}
    	void set_time();
    	void show_time();
    private:
    	int hour;
    	int minute;
    	int sec;
    };
    
    void Time::set_time()
    {
    	cin >> hour >> minute >> sec;
    }
    
    void Time::show_time()
    {
    	cout << hour << ":" << minute << ":" << sec << endl;
    }
    
    int main()
    {
    	Time t1;
    	t1.set_time();
    	t1.show_time();
    	Time t2;
    	t2.show_time();
    	return 0;
    }

    这是构造函数的一个很简单的运用 这里的构造函数的功能是 给对象的数据成员赋初值为0  建立对象t1时调用构造函数  对象t1再调用了set_time函数 重新赋了一次值  而t2中的数据成员的值都是0

    构造函数同样可以在类内声明  在类外定义  同样需要声明构造函数的所属类 以及作用于限定符  即

    9.1.3  带参数的构造函数

    引:上方给出的构造函数,是十分死板的。每个建立的对象都有同样的初始化值,无法达到不同对象不同初始化值的效果。所以有了带参数的构造函数。

    即构造函数的参数列表中加若干参数 并在函数体中对不同数据成员赋值。 这样在建立对象并执行构造函数时,就可以把定义对象时给的一些数据赋给数据成员。

    构造函数的实参只能在定义对象时给出,即建立对象的同时给出数据成员的初值。

    // 带参数的构造函数
    class Cube
    {
    public:
    	int volume();
    	Cube(int, int, int);
    private:
    	int length;
    	int width;
    	int height;
    };
    Cube::Cube(int a, int b, int c)
    {
    	length = a;
    	width = b;
    	height = c;
    }
    int Cube::volume()
    {
    	return length * width * height;
    }
    int main()
    {
    	//Cube c1 = { 10,20,30 };		
    	Cube c1(10, 20, 30);   	//建立对象c1 并指定c1的长宽高  直接在后面加括号就行;
    	cout << "c1的体积为:" << c1.volume() << endl;
    	Cube c2(10, 10, 10);
    	cout << "c2的体积为:" << c2.volume() << endl;
    	return 0;
    }

    带参数的构造函数的声明以及类对象的定义方法如上所示,注意main函数中第一行第二行的方式都可以, 第二种可以看成给构造函数的形参赋实参。类似于函数调用。

     9.1.4  用参数初始化表对数据成员初始化

    不在构造函数函数体内执行初始化语句,放在函数首部实现。让构造函数显得简短精炼,就可以在类体内定义构造函数而不在类外定义。尤其当数据成员较多时更显其优越性,许多c++程序人员都喜欢用这种方式初始化所有数据成员。

    // 参数初始化表对数据成员初始化
    #include<string.h>
    class cube
    {
    public:
    	cube(int l, int w, int h,string n) :length(l), width(w), height(h) ,name(n)  //参数初始化表
    	{
    		//strcpy(arr, brr);
    	}
    	void show();
    private:
    	int length;
    	int width;
    	int height;
    	//char arr[10];
    	string name;
    };
    
    void cube::show()
    {
    	cout << name << ":" << length * width * height << endl;
    }
    int main()
    {
    	cube c1(10, 20, 30, "yang");
    	c1.show();
    	
    	cube c2(10, 10, 10, "li");
    	c2.show();
    	return 0;
    }

    格式如上,即: 在构造函数的函数首部后方再加一个冒号后面给出对应的值 

    注意:对于数组的初始化不可以像其他数据那样 因为数组不可以直接用等于号赋值(string可以)需要用strcpy这样的字符数组操作函数进行初始化。 

    9.1.5  构造函数的重载

    即多个构造函数同名,但参数的个数不同,或参数的类型不同的构造函数。称为构造函数的重载

    class Box
    {
    public:
    	int volume();
    	Box(int l, int w, int h) :length(l),width(w),height(h){}  //有三个参数的构造函数
    	Box(int l,int w):length(l),width(w)                       //有两个参数的构造函数
    	{
    		height = 10;
    	}
    	Box(int l) :length(l)									  //有一个参数的构造函数
    	{
    		width = 10;
    		height = 10;
    	}
    	Box();													  //无参构造函数
    private:
    	int length;
    	int width;
    	int height;
    };
    
    int Box::volume()
    {
    	return length * height * width;
    }
    
    Box::Box()
    {
    	length = 10;
    	width = 10;
    	height = 10;
    }
    
    int main()
    {
    	Box b1;
    	cout << "b1的体积为:" << b1.volume() << endl;
    	Box b2(20, 20, 20);
    	cout << "b2的体积为:" << b2.volume() << endl;
    	Box b3(20, 20);
    	cout << "b3的体积为:" << b3.volume() << endl;
    	Box b4(20);
    	cout << "b4的体积为:" << b4.volume() << endl;
    	return 0;
    }

    9.1.6  使用默认参数的构造函数 

    构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值。

    class cube
    {
    public:
    	cube(int l = 10, int w = 10, int h = 10):length(l),width(w),height(h){}
    	void show();
    private:
    	int length;
    	int width;
    	int height;
    };
    
    void cube::show() 
    {
    	cout << "length=" << length <<"  " << "width=" << width <<"  " << "height=" << height << endl;
    }
    
    int main()
    {
    	cube c1(20, 20, 20);
    	c1.show();
    	cube c2(20, 20);
    	c2.show();
    	cube c3(20);
    	c3.show();
    	cube c4;
    	c4.show();
    	return 0;
    }
    
    

    可以看到,在构造函数中使用默认参数是方便而有效的,它提供了建立对象时的多种选择,它的作用相当于好几个重载的构造函数。它的好处是:即使在调用构造函数时没有提供实参值,不仅不会出错,而且还确保按照默认的参数值对对象进行初始化。尤其在希望对每一个对象都有同样的初始化状况时用这种方法更为方便,不需输人数据,对象全按事先指定的值进行初始化。

    注意:


    (1)在建立对象时不必给出实参的构造函数,称为默认构造函数( defaultconstructor)。显然,无参构造函数属于默认构造函数。一个类只能有一个默认构造函数。如果用户未定义构造函数,则系统会自动提供一个默认构造函数,但它的函数体是空的,不起初始化作用。如果用户希望在创建对象时就能使数据成员有初值,就必须自己定义构造函数。

    (2)  应该在什么地方指定构造函数的默认参数?应在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。因为类声明是放在头文件中的,它是类的对外接口,用户是可以看到的,而函数的定义是类的实现细节,用户往往是看不到的。(类声明和类定义会放在两个文件中)。在声明构造函数时指定默认参数值,使用户知道在建立对象时怎样使用默认参数。

    (3)程序第5行在声明构造函数时,形参名可以省略,即写成Box ( int = 10 , int = 10 , int = 10);

    (4)如果构造函数的全部参数都指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参。由于不需要实参也可以调用构造函数,因此全部参数都指定了默认值的构造函数也属于默认构造函数。前面曾提到过:一个类只能有一个默认构造函数,也就是说,可以不用参数而调用的构造函数,一个类只能有一个。其道理是显然的,是为了避免调用时的歧义性。

    (5)在一个类中定义了全部是默认参数的构造函数后,不能再定义重载构造函数。自行理解为什么

    (6)  但如果构造函数中的参数并非全部为默认值时,(即部分指定默认值)就要分析具体情况。不是一定会出错,是很容易出错,要十分仔细。因此,一般不应同时使用构造函数的重载和有默认参数的构造函数。

    总结:

    (上方6点是我复制的= =) 构造函数,就是一个在对象建立时自动执行的函数,作用就是方便给对象的数据成员赋初值。 核心就是有参数的构造函数以及有默认参数的构造函数。 我认为这种是最方便的,在建立对象时可直接指定该对象的数据成员的值。

    析构函数

    析构函数是与构造函数作用相反的函数  当对象的生命期结束时,会自动执行析构函数。

    析构函数不返回任何值,没有函数类型,也没有函数参数。由于没有函数参数,因此它不能被重载。一个类可以有多个构造函数,但是只能有一个析构函数。
    实际上,析构函数的作用并不仅限于释放资源方面,它还可以被用来执行“类的设计者希望在最后一次使用对象之后所执行的任何操作”,例如输出有关的信息。

    如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数,但它只是徒有析构函数的名称和形式,实际上什么操作都不进行。想让析构函数完成任何工作,都必须在定义的析构函数中指定。

     调用构造函数和析构函数的顺序

    一般情况下: 先构造的后析构,后构造的先析构。

     构造函数和析构函数在面向对象的程序设计中是相当重要的,是类的设计中的一个重要部分。

    展开全文
  • 指针只是指向了一个内存地址,但是当存内存中取值的时候,系统不知道你要从当前指针指向的地址,取几个字节,指定了指针的类型后,系统就知道取几个字节了。 char类型取1个字节,short类型取2个字节,int类型去4个...

    1. 指针类型的作用

    1.1 指针取出字节

    • 任何类型的指针占用的空间大小都是相同的
      (32位CPU是4字节;64位CPU是8字节)

    • 既然任何类型的指针占用的空间大小都是相同的,为什么指针还需要类型呢?

    • 指针只是指向了一个内存地址,但是当存内存中取值的时候,系统不知道你要从当前指针指向的地址,取几个字节,指定了指针的类型后,系统就知道取几个字节了。

    • char类型取1个字节,short类型取2个字节,int类型去4个字节。

    注意:

    1. bit 是计算机最小的单位; 一个字节 = 8 bit;

    2. 一个十六进制符 需要四位才能表示出来;
      0xf: 十六进制中的 一个16进制数, 比如 f , 需要用四位 二进制才能表示处理;
      十六进制数 f f f : 需要四位表示出来: 1111;

    所以一个十六进制符占用的是 半个字节,
    两个十六进制符 才是占用的一个字节;

    * char字符存储空间为一个字节,
     * 16进制的每个字符需要用4位二进制位来表示,
     * 0x0为0000,0xf为1111,即1个16进制数为4位,
     * 如42 4D 38 04 04 00 00 00 00 00 36 04 00 00,每两个16进制数隔开,
     * 用意是:因为1个16进制数为4位,两个就是8位,
    所以两个16进制数 占用8位, 占用了一个
    
     * 即1个字节,所以这里是14字节,,以字节为单位,容易计数
    
    
    int main() {
           int i = 123456789;//i的值用16进制表示就是:0x075bcd15
    
    
            int *p = &i;
            printf("int:%d\n", *p);//打印出123456789
    
    
            //char *c = (char*)&i;
            char *c;
            c = (char*)&i;
    
            //打印出21,因为是char类型的指针,所以取出1个字节,也就是去的是16进制的15,十进制就是21.
            printf("char:%d\n", *c);
    
            //打印出-13035,因为是short类型的指针,所以取出2个字节也就是去的是16进制的cd15,十进制就是-13035
            short *s = (short*)&i;
            printf("short:%d\n", *s);
    
    }
    

    在这里插入图片描述

    不同的指针类型,可以指示编译器怎样解释特定地址上内存的内容以及该内存区域应该跨越多少内存单元。

    如果一个int 型的指针:
    寻址到1000 内存处,
    那么在32 位机器上跨越的地址空间是1000~1003;

    如果一个double 型的指针:
    寻址到1000 内存处,
    那么在32 位机器上跨越的地址空间是1000~1007;

    1.2 指针的定义

    指针p也是对象,它同样有地址&p和存储的值p,
    只不过,p存储的数据类型是数据的地址。

    指针在定义的时候:
    声明该变量, 是指针类型的变量:

      int i = 3;
      int* p = &i
    

    但是在使用的时候, 称作是 解地址符: 意思是取出该指针中所保存的地址1, 找到该地址1, 取出地址1中的值;

     int value_i = *p 
     cout<< value_i<< endl;
    
    

    2. 构造函数

    构造函数是指一个特殊的公共成员函数, 它的作用是在创建类的对象时会自动调用,从而用于构造类的对象;

    构造函数的特点:

    1. 是指它的函数名称 与所属类的名称相同, 从而编译器知道这是类中一个特殊的成员函数;

    2. 构造函数不允许有返回类型, 除此之外, 构造函数和其他类的成员函数相似;

    3. 有个特点, 通常类名称首字母大写, 所以构造函数的首字母也会大写;

    从而函数名称与类名称是否相同, 函数名称首字母是否大写, 是否有无函数返回类型, 这三点,可以区分普通成员函数和构造函数;

    所以,我们程序员在创建类的时候, 我们应该自己定义出构造函数;
    在自己定义了构造函数后,
    好处是,在初始化类的对象时, 可以直接对类中的成员变量赋值;

    若没有自定义构造函数,调用系统自动生成的构造函数时, 在初始化对象时,不可以直接对类中的成员变量赋值;

    如果程序员没有编写构造函数,则 C++ 会自动提供一个,这个自动提供的构造函数永远不会有人看到它,但是每当程序定义一个对象时,它会在后台静默运行。

    构造函数的类型:

    1. 有形参的普通自定义构造函数;
    2. 没有形参的 的默认构造函数;
    3. 有形参的默认构造函数, 但是形参都设置了默认指, 无实参可以调用;

    注意,一般构造函数可以有多种参数形式,
    一个类可以有多个普通构造函数,前提是参数的个数或者类型不同(C++的函数重载机制)

    但是, 默认构造函数只能有且只有一个

    程序员通常使用构造函数来初始化对象的成员变量。但实际上,它可以做任何正常函数可以做的事情。

    举个例来说明有 无自定义构造函数区别:
    单链表中的构造函数:

    struct ListNode{
    int val;
    
    ListNode *next;
    // 节点的构造函数
    ListNode (int x): val(x), next(NULL){}
    };
    

    因为上述结构体中, 自己定义了 构造函数:

    所以在初始化的时候, 可以直接给变量赋值;

    ListNode* head = new ListNode(5);
    

    而如果没有自己定义构造函数, 则c++ 默认生成一个构造函数,
    但是 使用默认生成的构造函数时, 在初始化的时候,不可以给变量赋值;

    2.1 在类中定义构造函数

    下面的程序包括一个名为 Demo 的类,其构造函数除了打印消息之外什么都不做。编写它的目的就是为了演示构造函数何时执行。因为 Demo 对象是在两个 cout 语句之间创建的,所以构造函数将在这两个语句生成的输出行之间打印它的消息。

    // This program demonstrates when a constructor executes.
    #include <iostream>
    using namespace std;
    class Demo
    {
        public:
            Demo()
            {
                cout << "Now the constructor is running.\n";
            }
    };
    int main()
    {
        cout << "This is displayed before the object is created. \n";
        Demo demoObj;    // Define a Demo object
        cout << "This is displayed after the object is created.\n";
        return 0;
    }
    

    程序中,将构造函数定义为类声明中的内联函数;

    程序输出结果为:
    This is displayed before the object is created.
    Now the constructor is running.
    This is displayed after the object is created.
    

    2.2 在类外定义构造函数

    当然,像任何其他类成员函数一样,也可以将其原型放在类声明中,然后将其定义在类之外。

    在这种情况下,需要添加:

    1. 函数所属类的名称和
    2. 函数名前面的作用域解析运算符。

    但是由于构造函数的名称与类名相同,所以名称会出现两次。

    Demo:: Demo ()    // 构造函数
    {
        cout << "Now the constructor is running. \n";
    }
    

    2.3 重载构造函数

    我们知道,当两个或多个函数共享相同的名称时,函数名称被称为重载。只要其形参列表不同,C++ 程序中可能存在具有相同名称的多个函数。

    任何类成员函数都可能被重载,包括构造函数。例如,某个构造函数可能需要一个整数实参,而另一个构造函数则需要一个 double,甚至可能会有第三个构造函数使用两个整数。只要每个构造函数具有不同的形参列表,则编译器就可以将它们分开。

    下面的程序声明并使用一个名为 Sale 的类,它有两个构造函数。第一个构造函数的形参接受销售税率;第二个构造函数是免税销售,没有形参。它将税率设置为 0。这样一个没有形参的构造函数称为默认构造函数。

    #include <iostream>
    #include <iomanip>
    using namespace std;
    // Sale class declaration
    class Sale
    {
        private:
            double taxRate;
        public:
            Sale(double rate) // Constructor with 1 parameter
            {
                taxRate = rate; // handles taxable sales
            }
            Sale ()    // Default constructor
            {
                taxRate = 0.0    // handles tax-exempt sales
            }
            double calcSaleTotal(double cost)
            {
                double total = cost + cost*taxRate;
                return total;
            }
    };
    int main()
    {
        Sale cashier1(.06); // Define a Sale object with 6% sales tax
        Sale cashier2;    // Define a tax-exempt Sale object
       
        // Format the output
        cout << fixed << showpoint << setprecision (2);
        
        // Get and display the total sale price for two $24.95 sales
        cout << "With a 0.06 sales tax rate, the total of the $24.95 sale is $ \n";
        cout << cashier1.calcSaleTotal(24.95) << endl;
        
        
        cout << "\n On a tax-exempt purchase, the total of the $24.95 sale is, of course, $\n";
        cout << cashier2.calcSaleTotal(24.95) << endl;
        return 0;
    }
    

    输出结果:

    With a 0.06 sales tax rate, the total of the $24.95 sale is $26.45
    
    On a tax-exempt purchase, the totalof the $24.95 sale is, of course, $24.95
    

    注意看此程序如何定义的两个 Sale 对象:

    Sale cashier1(.06);
    Sale cashier2;
    

    在 cashier1 的名称后面有一对括号,用于保存值,发送给有一个形参的构造函数。但是,在 Cashier2 的名称后面就没有括号,它不发送任何参数。在 C++ 中,当使用默认构造函数定义对象时,不用传递实参,所以不能有任何括号,即:

    Sale cashier2 () ;    // 错误
    Sale cashier2;    // 正确
    
    

    构造函数和默认构造函数的 区别是:
    当类的对象创建时, 但没有给对象的成员变量 初始化赋值时,
    则编译器,会自动调用默认构造函数;

    通常情况下,默认构造函数没有形参;

    2.3 无形参的默认构造函数

    为了创建不传递任何参数的对象,必须有一个不需要参数的构造函数,也就是默认构造函数。

    如果没有这样一个默认构造函数,那么当程序尝试创建一个对象而不传递任何参数时,它将不会编译,这是因为必须有一个构造函数来创建一个对象

    如果程序员没有为类编写任何构造函数,则编译器将自动为其创建一个默认构造函数。

    但是,当程序员编写了一个或多个构造函数时,即使所有这些构造函数都是有参数的,编译器也不会创建一个默认构造函数,所以程序员有责任这样做。

    那么,在设计一个具有构造函数的类时,应该包括一个默认构造函数,这在任何时候都会被认为是一个很好的编程实践。

    类可能有许多构造函数,但只能有一个默认构造函数。
    这是因为:如果多个函数具有相同的名称,则在任何给定时刻,编译器都必须能够从其形参列表中确定正在调用哪个函数。
    它使用传递给函数的实参的数量和类型来确定要调用的重载函数

    因为一个类名称只能有一个函数能够接受无参数,所以只能有一个默认构造函数。

    2.3 有形参的默认构造函数

    一般情况下,就像在 Sale 类中那样,默认构造函数没有形参。

    Sale 类需要一个默认构造函数来处理免税销售的情况,但是其他类可能并不需要这样一个构造函数。

    例如,如果通过类创建的对象总是希望将实参传递给构造函数。

    所以有另一种的默认构造函数,其所有形参都具有默认值,所以,它也可以无实参调用。

    如果创建了一个接受无实参的构造函数,同时又创建了另外一个有参数但允许所有参数均为默认值的构造函数,那么这将是一个错误,因为这实际上是创建了两个“默认”构造函数。以下语句就进行了这种非法的声明:

    class Sale //非法声明,  不应该存在两个 默认构造函数;
    {
        private:
            double taxRate;
        public:
            Sale()    //无实参的默认构造函数
            {
                taxRate = 0.05;
            }
            Sale (double r = 0.05)    //有默认实参的默认构造函数
            {
                taxRate = r;
            }
            double calcSaleTotal(double cost)
            {
                double total = cost + cost * taxRate;
                return total;
            };
    };
    

    可以看到,第一个构造函数没有形参,第二个构造函数有一个形参,但它有一个默认实参。如果一个对象被定义为没有参数列表,那么编译器将无法判断要执行哪个构造函数。

    2.4 各自构造函数的写法

    2.4.1 有形参的普通构造函数

    具有形参的普通构造函数,可以有两种写法:

    1. 初始化列表的方式:

    构造函数名称( 初始化值1, 初始化值2 ):成员变量1(初始化值1),… 成员变量n(初始化值n){}

    class Student{
    public:
    int m_age;
    int m_score;
    
    // 列表方式,定义构造函数;
    Student(int age, int score): m_age(age), m_socre(score){}
    
    };
    
    
    1. 内部赋值方式:
      正常函数的赋值
    Student(int age, int score){
       m_age = age;
       m_score = score;
    }
    

    C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。也就是说采用初始化列表的话,构造函数本体实际上不需要有任何操作,因此效率更高。

    2.4.1 无形参的构造函数

    函数内部赋值

    Student(){
       m_age = 0;
       m_score = 0;
    }
    

    3. 拷贝构造函数

    拷贝构造函数 为类对象本身的引用,
    根据一个已经存在的对象复制出一个新的对象,一般在函数中,会将已经存在对象的数据成员的值复制一份到新创建的对象中;

    
    Student(Student& S){
       m_age = s.m_age;
       m_score = s.m_score;
        cout << " 这是个 复制构造函数" << endl;
       
    }
    
    

    注意:若没有显示定义复制构造函数,则系统会默认创建一个复制构造函数,当类中有指针成员时,由系统默认创建的复制构造函数会存在“浅拷贝”的风险,因此必须显示定义复制构造函数。

    • 浅拷贝指的是在对对象复制时,只对对象中的数据成员进行简单的赋值,若存在动态成员,就是增加一个指针,指向原来已经存在的内存。这样就造成两个指针指向了堆里的同一个空间。当这两个对象生命周期结束时,析构函数会被调用两次,同一个空间被两次free,造成野指针。

    • 深拷贝就是对于对象中的动态成员,不是简单的赋值,而是重新分配空间。

    4. 析构函数

    析构函数是具有与类相同名称的公共成员函数,前面带有波浪符号(〜)。例如,Rectangle 类的析构函数将被命名为 〜Rectangle。

    当对象被销毁时,会自动调用析构函数。在创建对象时,构造函数使用某种方式来进行设置,那么当对象停止存在时,析构函数也会使用同样的方式来执行关闭过程。

    4.1 析构函数的特点

    除了需要知道在对象被销毁时会自动调用析构函数外,还应注意以下事项:

    • 像构造函数一样,析构函数没有返回类型。
    • 析构函数不能接收实参,因此它们从不具有形参列表。
    • 由于析构函数不能接收实参,因此只能有一个析构函数。

    例如,当具有对象的程序停止执行或从创建对象的函数返回时,就会发生这种情况。

    下面的程序显示了一个具有构造函数和析构函数的简单类。它说明了在程序执行过程中这两个函数各自被调用的时间:

    //This program demonstrates a destructor.
    #include <iostream>
    using namespace std;
    class Demo
    {
        public:
            Demo(); // Constructor prototypeDemo(); // Destructor prototype
    };
    Demo::Demo()    // Constructor function definition
    {
        cout << "An object has just been defined,so the constructor" << " is running.\n";
    }
    Demo::Demo() // Destructor function definition
    {
        cout << "Now the destructor is running.\n";
    }
    int main()
    {
        Demo demoObj;    // Declare a Demo object;
        cout << "The object now exists, but is about to be destroyed.\n";
        return 0;
    }
    
    程序输出结果:
    An object has just been defined, so the constructor is running.
    The object now exists, but is about to be destroyed.
    Now the destructor is running.
    
    展开全文
  • 的对象被创建的时候,编译系统对该对象分配内存空间,并,完成成员的...C++的构造函数可以有多,创建对象时编译器会根据传入的参数不同调用不同的构造函数构造函数的特点以类名作为函数名,无返回类型。...
  • C++ 构造函数

    2021-05-22 04:12:27
    构造函数是成员函数的种特殊类型,它在创建对象时会自动对其进行初始化。编译器通过其名称和返回类型将给定的成员函数标识为构造函数构造函数与该具有相同的名称,并且没有任何返回类型。同样,构造函数始终是...
  • C++:构造函数与重载构造函数

    千次阅读 2020-10-18 22:21:01
    本文主要总结了默认构造函数的相关用法和构造函数重载,旨在能够对平时的项目开发起到一定的夯实基本功的作用,言简意赅,一目了然。 一、构造函数的定义 ...在一个类中可以定义多个构造函数,以便提
  • C++ 构造函数和析构函数

    千次阅读 2020-06-14 10:06:34
    一、单选题 2-1在的定义中,用于为对象分配内存空间,对的数据成员进行初始化并执行其他内部管理操作的函数是 C A 友元函数 ...A 一个类只能有一个对象 B 对象是的具体实例 C 是对某一对象的抽象
  • 默认构造函数构造函数重载

    万次阅读 多人点赞 2017-09-29 08:15:52
    对象被创建时,编译器为对象分配内存空间,并自动调用该构造函数,由构造函数完成数据成员的初始化工作。、默认构造函数 从代码层面进行分析 class testClass { public : testClass();//不带参数的默认
  • 、拷贝构造函数的形式 复制构造函数构造函数种特殊情况。因为的对象包含各种成员变量,在发生拷贝时不能和普通对象一样来拷贝,所以我们需要使用拷贝构造函数来进行对象拷贝。拷贝构造函数只有...
  • 什么是构造函数及定义

    万次阅读 多人点赞 2021-03-07 20:47:43
    什么是构造函数 建立一个对象时,通常最需要立即做的工作是初始化对象,如对数据成员赋初值 构造函数就是用来在创造对象时初始化对象,为对象数据成员赋初始值
  • 通过本题目的练习可以掌握构造函数的定义; 设计一个Point,私有数据成员有x、y;公有成员函数有:无参数的构造函数Point(),带参数的构造函数Point(int,int);ShowPoint()输出点对象的信息 在主函数main()...
  • 7.构造函数和方法

    千次阅读 2019-03-07 16:36:39
    1.构造函数的规则和语法; 2.创建对象时内存的分配情况; 3.构造函数的使用; 4.方法签名和调用; 5.构造函数和方法参数的两种参数传递方式(值传递和引用传递) 6.变量作用域和关键字(this/super)
  • String支持几种构造函数。...使用如下的构造函数可以创建一个被字符数组初始化的字符串(String): String(char chars[ ]) 下面是一个例子: char chars[] = { 'a', 'b', 'c' }; String s = new
  • 作者:simon_syeming  本文主要总结了默认构造函数的相关用法和构造函数重载,旨在能够对平时的项目开发起到一定的夯实基本功的...该对象被创建时,编译器为对象分配内存空间,并自动调用该构造函数,由构造...
  • 构造函数是属于某一个类是一种抽象的数据类型,它不占存储空间,不能容纳具体的数据,因此在声明中不能给数据成员赋初值。 例如,下面的描述是错误的: class Complex{ double real=0; //错误,在声明...
  • C++构造函数详解及显示调用构造函数

    万次阅读 多人点赞 2018-10-08 10:55:11
    c++构造函数详解 构造函数是干什么的 class Counter { public:  // Counter的构造函数  // 特点:以类名作为函数名,无返回类型  Counter()  {  ...
  • 在现代社会信息化高速发展的今时今日,几乎每个人都在为了更好地生活而努力拼搏着。为了更高的薪资,很多人开始学习java编程语言。今天就来为大家介绍java中构造函数的特点是...特别的是一个类可以有多个构造函数...
  • 【C++】利用构造函数对象进行初始化

    千次阅读 多人点赞 2018-10-06 21:02:00
    运行环境:VS2017 一、对象的初始化 每一个对象都应当在它...这种是错误的,并不是一个实体,并不占储存空间,显然无处容纳数据。 如果一个类是公用的,可以在定义对象时对数据成员进行初始化。 class Time ...
  • 默认构造函数,如果没有定义构造函数 默认析构函数,如果没有定义 复制构造函数,如果没有定义,java参见:https://blog.csdn.net/ShewMi/article/details/80788591 赋值运算符,如果没有定义 地址运算符,如果...
  • 在目前的C++标准中,派生把基类全部的成员(不包括构造函数和析构函数)接收过来,也就是说是没有选择的,不能说选择接受一部分成员,而舍弃另一部分成员。 此外,可以在派生中声明一个与基类成员同名的成员,但...
  • C++:构造函数详解

    千次阅读 2019-03-02 18:11:00
    转自https://www.cnblogs.com/Renyi-Fan/p/8209043.html 构造函数是干什么的 class Counter { public: // Counter的构造函数 // 特点:以类名作为函数名,无返回类型 Counter() { m_value =...
  • 一个类中可以定义多个构造函数,以便为对象提供不同的初始化的方法,供用户选用,这些构造函数具有相同的名字,而参数的个数或参数的类型不相同,这称为构造函数的重载(函数重载)。 #include<iostream>...
  • Java之构造函数

    2022-03-29 12:53:20
    建立一个对象时,通常最需要立即做的工作是初始化对象,如对数据成员赋初值 (1)构造函数就是用来在创造对象时初始化对象,为对象数据成员赋初始值 (2)构造函数一般声明为公有的(public),因为创建对象通常是...
  • c++的静态成员,构造函数

    千次阅读 2017-03-31 15:17:26
    静态成员是处理同一个类的不同对象之间数据和函数共享问题的。 1.静态数据成员 我们所说的“一个类的所有对象具有相同的属性”是指实例属性,以非静态数据成员表示,如Employee中的empNo.id.它们在的每一个对象...
  • JavaScript 内置构造函数 ...内置构造函数:内置构造函数...创建一个Boolean 类型的数据,与数组一样也具有三种方式 // 直接量方式 var b1 = true; // 构造函数方式(具有数据类型转换的功能) var b2 = new Boolean(true)
  • 构造函数和析构函数

    2015-06-02 21:28:43
    1、构造函数 C++提供了构造函数(constructor)来处理对象的初始化。...构造函数没有返回值,因此也不需要在定义构造函数时声明类型,这是它和一般函数的一个重要的不同之点。 如果构造函数不带参数,在函
  • **Java中的构造方法(构造函数)与...特别的,一个类是可以有多个构造函数,可以根据参数个数的不同或者参数类型的不同来区分它们,即为构造函数的重载。构造方法是一个特殊的成员,在实例化时会自动调用,其构...
  • C#的 构造函数 和 方法重载

    千次阅读 2020-04-10 16:11:23
    构造函数(一本正经的讲构造函数 如果想看不正经的往下翻看方法重载) 方法名称与类名相同,没有返回值类型,连void都没有 用作给的对象初始化 一个类中可以有多个构造 如果手动添加一个构造,系统不会自动生成无参...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 118,295
精华内容 47,318
热门标签
关键字:

一个类可包含构造函数的个数