-
2022-02-07 20:15:48
构造函数:
1. 构造函数在创建对象时被调用。
2. 无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。
3. 构造函数的名称和类名相同。
4. 可以创建多个同名的构造函数,条件是每个函数的参数列表都不同。
5. 构造函数没有声明类型。
6. 通常构造函数用于初始化类对象成员,初始化应与构造函数的参数列表匹配。
7. 构造函数的参数不是类的成员,而是赋给类成员的值。
8. 默认构造函数没有参数,如果程序没有提供任何构造函数,编译器提供默认构造函数;如果程序有其它构造函数,则由程序员自己提供默认构造函数。
假如定义了一个Stock类,以下是构造函数:
Stock::Stock()
{
}
Stock::Stock(参数1)
{
}
Stock::Stock(参数1,参数2)
{
}
Stock::Stock(参数1,参数2,...)
{
}
调用时根据参数类型匹配构造函数。
例:
Stock Stock1("Nano", 12, 20.0);
调用构造函数 Stock::Stock(const char * co, int n, double pr), 创建一个名为 Stock1的对象。
Stock Stock2 = Stock("Nano", 12, 20.0);
调用构造函数 Stock::Stock(const char * co, int n, double pr), 创建一个名为 Stock2的对象。根据编译器不同,可能会产生一个临时对象,在赋值给Stock2后丢弃临时对象。
Stock2 = Stock("Nano", 12, 20.0);
Stock2已存在,调用Stock::Stock(const char * co, int n, double pr)产生一个临时对象,在赋值给Stock2后丢弃临时对象。
Stock Stock3;
调用默认构造函数 Stock::Stock(), 创建一个名为 Stock3的对象。
Stock Stock3 = Stock();和 Stock *pr = new Stock;
都是调用默认构造函数 Stock::Stock()。
注意: Stock Stock3 (); 是定义了一个返回为Stock的函数,不是构造对象。
析构函数:
1. 在删除对象时调用析构函数。
2. 每个类只有一个析构函数。
3. 析构造函数的名称为类名前加~。
4. 如果构造函数使用了new,则必须提供使用delate的析构函数。
5. 析构函数没有返回类型,包括void类型也没有。
假如定义了一个Stock类,以下是析构函数:
Stock::~Stock()
{
}
参考 C++ Primer Plus
更多相关内容 -
C++ 构造函数中使用new时注意事项
2020-12-31 00:20:29当然在构造函数中使用new初始化指针的时候,可以把指针初始化为空(0/NULL 或者是C++11中的nullptr),因为delete不管有没带[]都与空指针兼容。 需要自行定义一个复制构造函数和赋值构造函数,用深复制的方式把一个... -
c++ 构造函数详解
2019-05-31 17:20:58c++构造函数详解。(构造函数的分类、拷贝构造函数)c++ 构造函数详解
构造函数是干什么的
- 该类对象被创建的时候,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作,故:构造函数的作用:初始化对象的数据成员
构造函数的分类
- 无参构造函数
- 带默认值的构造函数
- 有参(无默认值)的构造函数
- 复制构造函数(拷贝构造函数)
- 一种特殊的构造函数,当对象之间复制时会自动调用拷贝构造函数
- 若类中没有显示定义拷贝构造函数,则系统会自动生成默认拷贝构造函数
#include <iostream> using namespace std; class Coordinate { public: // 无参构造函数 // 如果创建一个类你没有写任何构造函数,则系统自动生成默认的构造函数,函数为空,什么都不干 // 如果自己显示定义了一个构造函数,则不会调用系统的构造函数 Coordinate() { c_x = 0; c_y = 0; } // 一般构造函数 Coordinate(double x, double y):c_x(x), c_y(y){} //列表初始化 // 一般构造函数可以有多个,创建对象时根据传入的参数不同调用不同的构造函数 Coordinate(const Coordinate& c) { // 复制对象c中的数据成员 c_x = c.c_x; c_y = c.c_y; } // 等号运算符重载 Coordinate& operator= (const Coordinate& rhs) { // 首先检测等号右边的是否就是等号左边的对象本身,如果是,直接返回即可 if(this == &rhs) return* this; // 复制等号右边的成员到左边的对象中 this->c_x = rhs.c_x; this->c_y = rhs.c_y; return* this; } double get_x() { return c_x; } double get_y() { return c_y; } private: double c_x; double c_y; }; int main() { // 调用无参构造函数,c1 = 0,c2 = 0 Coordinate c1, c2; // 调用一般构造函数,调用显示定义构造函数 Coordinate c3(1.0, 2.0); c1 = c3; //将c3的值赋值给c1,调用"="重载 Coordinate c5(c2); Coordinate c4 = c2; // 调用浅拷贝函数,参数为c2 cout<<"c1 = "<<"("<<c1.get_x()<<", "<<c1.get_y()<<")"<<endl <<"c2 = "<<"("<<c2.get_x()<<", "<<c2.get_y()<<")"<<endl <<"c3 = "<<"("<<c3.get_x()<<", "<<c3.get_y()<<")"<<endl <<"c4 = "<<"("<<c4.get_x()<<", "<<c4.get_y()<<")"<<endl <<"c5 = "<<"("<<c5.get_x()<<", "<<c5.get_y()<<")"<<endl; return 0; }
c1 = (1, 2) c2 = (0, 0) c3 = (1, 2) c4 = (0, 0) c5 = (0, 0) 请按任意键继续. . .
拷贝构造函数
- 拷贝构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类型的引用。当定义一个新对象并用同一类型的对象都它进行初始化时,将显示使用拷贝构造函数,当该类型的对象传递给函数返回该类型的对象时,将隐式调用拷贝构造函数
- 当类中有一个数据成员是指针时,或者有成员表示在构造函数中分配的其他资源,必须显示定义拷贝构造函数
- 构造函数的使用情况
- 一个对象以值传递的方式传入函数体
- 一个对象以值传递的方式从函数体返回
- 一个对象需要通过另一个对象进行初始化
#include <iostream> using namespace std; class Test { public: // 构造函数 Test(int a):t_a(a){ cout<<"creat: "<<t_a<<endl; } // 拷贝构造函数 Test(const Test& T) { t_a = T.t_a; cout<<"copy"<<endl; } // 析构函数 ~Test() { cout<<"delete: "<<t_a<<endl; } // 显示函数 void show() { cout<<t_a<<endl; } private: int t_a; }; // 全局函数,传入的是对象 void fun(Test C) { cout<<"test"<<endl; } int main() { Test t(1); // 函数中传入对象 fun(t); return 0; }
creat: 1 copy test delete: 1 delete: 1 请按任意键继续. . .
浅拷贝与深拷贝
- 浅拷贝
- 所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。也就是增加了一个指针,指向原来已经存在的内存。 正常情况下,“浅拷贝”已经能很好的工作,但是一旦对象存在动态成员,浅拷贝就会出问题。让我们考虑下面一段代码:
#include <iostream> #include <assert.h> using namespace std; class Test { public: Test(){ p = new int(10); } ~Test(){ assert(p != NULL); // assert()作用是如果他的条件返回错误,则终止程序执行 delete p; } private: int x; int y; int* p; }; int main() { Test t1; Test t2(t1); // 调用默认拷贝构造函数 return 0; }
上述程序崩溃。在使用t1复制t2时,进行的是浅拷贝,只是将成员的值进行赋值。此时,t1.p = t2.p, 即两个指针指向了堆里的同一个空间。这样,析构函数会被调用两次,这就是错误出现的原因。此问题的解决方法是“深拷贝”。
- 深拷贝
- 深拷贝就是对于对象中的动态成员,并不只是简单的赋值,而是重新分配空间,即资源重新分配。上述代码处理如下:
#include <iostream> #include <assert.h> using namespace std; class Test { public: Test(){ x = 0; y = 0; p = new int(10); } Test(const Test& t) { x = t.x; y = t.y; p = new int(10); *p = *(t.p); } ~Test(){ assert(p != NULL); // assert()作用是如果他的条件返回错误,则终止程序执行 delete p; } int get_x(){return x;} int get_y(){return y;} private: int x; int y; int* p; }; int main() { Test t1; Test t2(t1); // 调用默认拷贝构造函数 cout<<"("<<t1.get_x()<<", "<<t1.get_y()<<")"<<endl <<"("<<t2.get_x()<<", "<<t2.get_y()<<")"<<endl; return 0; }
(0, 0) (0, 0) 请按任意键继续. . .
此时t1与t2的p各自指向一段内存空间,但他们指向的内容相同,这就是“深拷贝”。
-
C++ 构造函数详解
2021-06-06 11:11:53C++中各类构造函数说明,含有样例。
前言
最近在复习C++的基础知识,在复习的时候才发现,对C++构造函数这方面的内容一直没有很清晰的了解,所以在此稍作记录。
一、构造函数
类的构造函数是类的一种特殊的成员函数,其任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
二、构造函数分类
在C++当中,构造函数主要可以分为一下四类:
1.默认构造函数
Test(); //无参
2.初始化构造函数
Test(int x,int y); //带参数,可初始化类内数据成员
3.复制(拷贝)构造函数
Test(const Test*temp); //通过另一个类成员初始化该成员
其中若非自己单独声明,编译器都会给我们隐形构造这几类函数。
设有个Test类内有如下数据成员,之后的样例都基于此:
class Test{ private: int x; int y; int* p; }
1.默认构造函数
因为此种构造函数无参,我们可以在其之后的内部进行赋值,当初始化语句如下时,将默认使用此构造函数:
//构造函数 Test() { x = 0; y = 0; p = new int(10); *p = 0; } //初始化方式 Test test1;
2.初始化构造函数
在这种构造函数当中,我们可以采用两种方式进行对类内成员的赋值,一种是列表初始化,而另一种是内部赋值,样例如下:
//构造函数(二者选一,不能共存) Test(int a,int b) { x = 0; y = 0; } Test(int a, int b) :x(a), y(b) {} //初始化方式 Test test1(10,20);
值得注意的是,C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。也就是说采用初始化列表的话,构造函数本体实际上不需要有任何操作,因此效率更高。
同时凭借C++的重载特性,根据不同形参也可有多个构造函数。
3.复制(拷贝)构造函数
该构造函数主要是使用一个已有的对象去初始化另一个对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中。样例如下:
//构造函数 Test(const Test& t) { x = t.x; y = t.y; p = t.p; } //初始化方式 Test t1; Test t2(t1);
但是运行此样例后我们会发现运行出错,其主要原因是浅拷贝,深拷贝问题。
由于成员数据中含有指针,当我们简单的将指针等于另一个对象的指针后,当程序最后执行析构函数时,将对指针进行回收,但是由于两个指针都指向同一地址,而同一地址的两次析构,就会产生错误,所以正确样例如下:
Test(const Test& t) { x = t.x; y = t.y; p = new int(10); *p=*(t.p); } //初始化方式 Test t1; Test t2(t1);
在第二个样例中我们可以发现,对指针重新开辟了一块地址,而p=(t.p)这条语句成功将第二个对象指针所指内容赋值给第一个对象。
所以在这时,是有两个对象,两个指针指向不同地址,但其内容相同。
再次从输出直观体现这一差别:
一般在类成员数据中含有指针都要考虑深浅拷贝的问题。
特殊"构造"函数
在查找相关资料的时候,发现很多博文在构造函数中加入了一类为:赋值构造函数,主要声明方式如下:
//函数声明 Test& operator=(const Test& t) { if (this == &t) return *this; delete p; x = t.x; y = t.y; p = new int; *p = *(t.p); } //初始化方式 Test t1(10,20); Test t2; t1=t2;
但自己总结后发现,这种操作只能称为赋值函数,而不是构造函数,因为当我们进行接下来的操作时:
Test t2=t1;
进行单步执行时发现,该语句只会执行上文的拷贝构造函数,而不是赋值函数。
但在构造函数的定义中,其是在类实例声明时即会调用,所以其并不能称为构造函数。
同时值得注意的是,在赋值函数中我们需要第一步首先判断是否是同一对象,防止循环引用,重复赋值等问题。
总结
在本文中,主要对C++中几类构造函数进行了介绍。
如文中出现纰漏,可在评论区和我交流。
同时指针操作需要记得释放内存,在此说明。 -
C++构造函数
2014-06-16 13:10:42C++构造函数,拷贝构造函数,虚函数,派生类 -
C++构造函数的种类及其作用
2022-03-13 20:43:53C++中有四类构造函数:默认构造函数、初始化构造函数、拷贝构造函数和移动构造函数。 当有了有参数的初始化构造函数,编译器就不会提供默认构造函数了。 默认构造函数和初始化构造函数: class Student { public...C++中有四类构造函数:默认构造函数、初始化构造函数、拷贝构造函数和移动构造函数。
当有了有参数的初始化构造函数,编译器就不会提供默认构造函数了。
只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数。
所以如果我们定义了其他的构造函数,我们必须手动显示地定义一个默认构造函数。- 默认构造函数和初始化构造函数:
class Student { public: //默认构造函数 Student() { num=1001; age=18; } //初始化构造函数 Student(int n,int a):num(n),age(a){} private: int num; int age; }; int main() { //用默认构造函数初始化对象S1 Student s1; //用初始化构造函数初始化对象S2 Student s2(1002,18); return 0; }
- 拷贝构造函数
#include<iostream> #include<string> using namespace std; class Student { public: Student() {cout<<"调用默认构造函数"<<endl;} Student(string name,string no):stuname(name),stuno(no) { cout<<"调用初始化构造函数"<<endl; } Student(const Student &s) { cout<<"调用拷贝构造函数"<<endl; this->stuname=s.stuname; this->stuno=s.stuno; } ~Student() {cout<<"调用析构函数"<<endl;} void printinfo(Student sss) { cout<<" student.stuname: "<<sss.stuname<<" student.stuno: "<<sss.stuno<<endl; } private: string stuname; string stuno; }; int main() { string s1,s2; s1="CRP"; s2="1906400038"; Student st(s1,s2); st.printinfo(st); Student st2(st); st2.printinfo(st2); return 0; }
3. 移动构造函数#include <iostream> using namespace std; class demo{ public: demo():num(new int(0)){ cout<<"construct!"<<endl; } demo(const demo &d):num(new int(*d.num)){ cout<<"copy construct!"<<endl; } //添加移动构造函数 demo(demo &&d):num(d.num){ d.num = NULL; cout<<"move construct!"<<endl; } ~demo(){ cout<<"class destruct!"<<endl; } private: int *num; }; demo get_demo(){ return demo(); } int main(){ demo a = get_demo(); return 0; }
num指针变量采用的是浅拷贝的复制方式,同时在函数内部重置了d.num,有效避免了程序结束后,同一块堆空间被多次释放。
-
C++构造函数之初始化列表
2022-03-04 10:07:39C++构造函数的初始化列表详解 -
c++构造函数和析构函数
2021-04-30 16:33:58一、构造函数和析构函数的特点 构造函数和析构函数是一种特殊的公有成员函数,每一个类都有一个默认的...C++类在创建时,系统会默认创建一个不带参数的构造函数,我们可以重新定义这个构造函数。默认构造函数在类被 -
C++构造函数(详细)
2020-06-14 15:00:13一、普通类的构造函数 class A { int a,b; public: A(){cout<<"默认构造函数"<<endl;}//自己定义的默认构造函数,什么也不坐 A(int x):a(x){cout<<"转换构造函数1"<<endl;}//转换构造... -
C++构造函数的三种写法
2021-05-18 10:51:06常见的构造函数有三种写法:无参构造函数一般构造函数复制构造函数C++的构造函数可以有多个,创建对象时编译器会根据传入的参数不同调用不同的构造函数。1、无参构造函数如果创建一个类,没有写任何构造函数,则系统... -
C++构造函数概念及使用方法
2020-07-12 16:31:36在c++的类中,构造函数是一种特殊的成员函数,在每次创建创建一个类的时候会默认调用构造函数进行初始化工作。 构造函数用来完成一些必要的初始化工作,有了构造函数之后,就无需再单独写初始化函数,并且也不必担心... -
C++构造函数的各种用法全面解析(C++初学面向对象编程)
2021-03-19 22:49:27类和对象是 C++ 的重要特性,它们使得 C++ 成为面向对象的编程语言。在C++类的学习当中,有一个用来初始化成员数据的函数即构造函数,本文重点分析的就是构造涵的各种用法和注意事项。 -
c++构造函数中调用构造函数
2019-02-27 19:21:01本人不是很懂c++,只是平时仿真的时候,用一点,对于其一些约定就不是很熟,于是今天就栽在一个bug上了。 示例代码如下: #include <iostream> using namespace std; class Sender{ public: ... -
C++构造函数和数组
2020-09-26 21:32:11// 构造函数和数组 #include<iostream> using namespace std;/*是一个语句*/ class CTest { public : int a; int m_=-1, n_=-1; public: /*没有返回值且和类同名的得构造函数可以定义在类里头(但是要区别... -
C++构造函数重载
2013-04-02 19:25:45重载构造函数,调用成员函数,供C++初学者理解构造函数重载的概念 -
C++构造函数的三种调用方法
2020-03-12 23:43:02C++构造函数的三种调用方法 1、拷贝参数传递, void test01() { Point p = Point(2, 3); cout << "p.x=" << p.x << endl; cout << "p.y=" << p.y << endl; cout << ... -
C++构造函数的几种写法
2019-02-27 10:35:02C++构造函数的几种写法 ** 主要借鉴了https://blog.csdn.net/zxc024000/article/details/51153743 之前学习了构造函数的方法,但是对于类似于初始化列表的方式不太理解,于是自己在网上查阅了一些博客之后,在这里... -
学习笔记 | C++构造函数分几类
2020-07-08 20:40:54类的构造函数 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型void,也不会返回 void。构造函数可用于为某些成员变量设置... -
C++构造函数的四种传参方式
2021-05-15 14:34:09今天看到书上对对象直接用=赋值调用构造函数,十分不解,浴室百度了一下发现有四种方式可以对构造函数传参,觉得这几种方式只是是写C++语言的人规定的四种形式,都可以编译使用。 // classes and uniform ... -
C++构造函数默认值设置
2018-11-15 10:10:35C++构造函数默认值设置构造函数默认值代码 构造函数默认值 C++类中构造函数设置默认值应当注意: C++类构造函数只能对排在最后的参数提供默认值; 既可以在构造函数的声明中,也可以在构造函数的实现中,提供... -
C++构造函数调用顺序
2019-06-24 09:49:26C++构造函数按下列顺序被调用: (1)任何虚拟基类的构造函数按照它们被继承的顺序构造; (2)任何非虚拟基类的构造函数按照它们被继承的顺序构造; (3)任何成员对象的构造函数按照它们声明的顺序调用; (4)类自己... -
c++构造函数参数带默认值
2020-06-03 19:36:53#ifndef MYTEST_H #define MYTEST_H #include using namespace std; class myTest { public: myTest(char*a=0);...如果有参构造函数参数有默认值,相当于调用默认构造函数 具体参考 C++之带有默认参数值的 -
C++构造函数前加上explicit关键字的作用
2020-03-13 16:33:40在构造函数前加上explicit关键字,表示该构造函数只能被显示调用,不能被隐式调用。 构造函数显示调用场景: T t1(value);//定义对象的同时,通过圆括号给出初值,显示调用构造函数 构造函数隐式调用场景: T t1=... -
C++构造函数中使用new
2020-06-18 18:08:24如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete。 new和delete必须相互兼容。new对应delete,new[]对应于delete[]。 如果有多个构造函数,则必须以相同的方式使用new,要么都带中括号,要么... -
C++构造函数初始化类对象
2019-04-12 15:53:51构造函数二.构造函数的分类三.构造函数的写法 一.构造函数 【1】构造函数不需要在定义时声明类型。 【2】构造函数不需要用户进行调用。 【3】可以在类内,也可以在类外构造函数;在类外构造函数时,需要在类内进行... -
c++ 构造函数初始化过程
2018-09-14 11:26:52每个类都会为它的对象定义初始化的方式,用一个或几个特殊的函数去控制对象的初始化,我们把这些特殊的函数叫做 构造函数。 那么,问题来了,构造函数作用是什么? 答:为了初始化对象的数据成员。就是简单。... -
C++构造函数的default和delete
2018-12-06 13:06:29C++11中,当类中含有不能默认初始化的成员变量时,可以禁止默认构造函数的生成, myClass()=delete;//表示删除默认构造函数 myClass()=default;//表示默认存在构造函数 当类中含有不能默认拷贝成员变量时,可以...