C++构造函数
#include <iostream> #include <string> using namespace std; class Student { /* 构造函数语法:类名(参数表){ } 注意:函数名与类名相同,没有返回值 构造函数在创建对象时,会自动执行,主要用来初始化 */ public: Student(const string& name1,int age1,int no1) { //注意:公有 name = name1; age = age1; no = no1; } private: string name; public: int age; int no; void who(void) { cout << "我叫" << name << endl; cout << "今年" << age << "岁" << endl; cout << "学号是:" << no << endl; } }; int main() { Student s("张三", 25, 10011); //创建对象并初始化 s.who(); return 0; }
构造函数可以重载,可以带缺省参数
缺省构造函数:如果类中没有定义构造函数,编译器会提供一个缺省构造函数
对于基本类型成员变量不做初始化
对于类 类型的成员变量,将会自动调用相应的无参构造函数来初始化
如果类中有构造函数,那么编译器就不会提供缺省的无参构造函数了
类型转换构造函数:
class A { public: A() { m_i = 0; } A(int i) { //类型转换构造函数,说明:可以用=方式在类外对私有成员进行赋值 //必须是单参构造函数 m_i = i; } void print() { cout << m_i << endl; } private: int m_i; }; int main() { A a; a.print(); a = 100; //先把100隐试转换为A类型,保存在一个临时变量,通过临时变量再对m_i赋值;=后面的100可以看做实参;再次调用某个构造函数 a.print(); }
class A { public: A() { m_i = 0; } explicit A(int i) { //类型转换构造函数 //必须是单参构造函数 //explicit修饰符:不能隐试转换,必须显示转换 m_i = i; } void print() { cout << m_i << endl; } private: int m_i; }; int main() { A a; a.print(); a = (A)100; //由于explicit,先把100显示转换为A类型,保存在一个临时变量,通过临时变量再对m_i赋值 a.print(); }
拷贝构造函数(复制构造函数):
用一个已经存在的对象作为同类对象的构造实参,创建新的副本对象时,会调用该类拷贝构造函数
格式:
类名(const 类名&){
...
}
class A { public: A(int i=0,int ii=0) { m_i = i; m_ii = ii; } A(const A& d) { //拷贝构造函数 //d 代表传过来的对象实参【--对象的引用】 cout << "执行了拷贝构造函数" << endl; //注意:类中所有成员都要赋值 m_i = d.m_i; m_ii = d.m_ii; } void print() { cout << m_i << endl; cout << m_ii << endl; } private: int m_i; int m_ii; }; int main() { A a(123,789); a.print(); A b(a); //实参是同类对象--调用拷贝构造函数 b.print(); A c = a; //与上面相同 c.print(); A e = A(a);//与上面相同 e.print(); }
如果类内有拷贝构造函数,就执行自己的拷贝构造函数;如果类内没有,编译器会自动产生一个拷贝构造函数,如下实例
#include <iostream> class A { public: A(int i = 0, int ii = 0) { m_i = i; m_ii = ii; } void print() { std::cout << m_i << std::endl; std::cout << m_ii << std::endl; } private: int m_i; int m_ii; }; int main() { A a(123, 789); a.print(); A b(a); //实参是同类对象--调用拷贝构造函数 b.print(); A c = a; //与上面相同 c.print(); A e = A(a);//与上面相同 e.print(); }
初始化列表:
普通构造函数:
A(int i=0,int ii=0) { m_i = i; m_ii = ii; }
初始化列表的构造函数:
A(int i=0,int ii=0):m_i(i),m_ii(ii) { }
//m_i用i来初始化,m_ii用ii来初始化
区别:
普通初始化:先给变量划出空间,再赋值
初始化列表:申请空间和赋值同时进行
以下场景必须使用初始化列表:
场景一:如果有类对象成员,并希望以有参方式对其进行初始化,则必须使用初始化列表显示指明成员子对象需要的构造函数
class A { public: A(int i,int ii=0) { m_i = i; m_ii = ii; cout << "A构造函数" << endl; } void print() { cout << m_i << endl; cout << m_ii << endl; } private: int m_i; int m_ii; }; class B { public: B():m_a(135) { //因为m_a是类对象成员,所以必须用初始化列表 //说明:在创建m_a对象时,用135来初始化,135赋值给i //显示指明成员子对象的初始化方式 //在创建B的对象时,先执行A的初始化函数,再执行B的初始化函数 cout << "B的无参构造" << endl; } private: A m_a; //类对象 }; int main() { B b; }
场景二:对const成员和引用成员的初始化:
C++11可以直接赋值
class A { public: A():m_i(100),m_ii(g) { //m_i和m_ii,是const和引用,只能用初始化列表 cout << "A构造函数" << endl; } private: const int m_i; int& m_ii; }; int main() { A a; }
初始化顺序:
class A { public: A(const char* str1):str(str1),len(str.size()) { cout << "A构造函数" << endl; } int len; const string str; //初始化顺序一般是根据声明顺序 }; int main() { A a("liming"); cout << a.str << endl; cout << a.len << endl; //由于在求长度时,str还没有被初始化,所以返回值是乱值 }
拷贝构造在拷贝指针变量时要注意的问题:
#include <iostream> using namespace std; class A { public: A(int i = 0) { m_pi = new int(i); } void print(void)const { cout << m_pi << endl; //打印指针地址 } ~A(void) { //析构函数--对象销毁时自动调用 cout << "析构函数" << endl; delete m_pi; } private: int* m_pi; }; int main() { A a(132); a.print(); A b = a; b.print(); //两个地址是相同,系统默认的拷贝只拷贝变量本身(浅拷贝,对于指针变量只是复制地址),两个指针变量指向同一个数据 //在执行析构函数时会出错:两个变量的地址是一样的,销毁第一次地址正常,销毁第二次地址就报错--因为这个地址变量已经不存在了 //system("pause"); //暂停函数 return 0; }
解决方法:利用深拷贝:拷贝指针指向的数据,而不是拷贝指针变量
此时需要自己写拷贝函数了,不能用编译器提供的拷贝函数了
#include <iostream> using namespace std; class A { public: class A(const A& that) { //深拷贝构造函数 m_pi = new int; //先给指针变量分配内存 //这个变量中的地址与传过来的就不一样了 *m_pi = *that.m_pi; //拷贝指针指向的数据 } A(int i = 0) { m_pi = new int(i); } void print(void)const { cout << m_pi << endl; } ~A(void) { cout << "析构函数" << endl; delete m_pi; } private: int* m_pi; }; int main() { A a(132); a.print(); A b = a; b.print(); //system("pause"); return 0; }
拷贝构造与拷贝赋值:
#include <iostream> using namespace std; class A { public: A& operator=(const A& that) { //拷贝赋值函数 cout << "拷贝赋值函数" << endl; if (&that != this) { //防止自赋值 *m_pi = *that.m_pi; //拷贝指针所指的数据 } return *this; } class A(const A& that) { m_pi = new int; *m_pi = *that.m_pi; } A(int i = 0) { m_pi = new int(i); } void print(void)const { cout << m_pi << endl; } ~A(void) { cout << "析构函数" << endl; delete m_pi; } private: int* m_pi; }; int main() { A a(132); a.print(); A b = a; //这是拷贝构造 b.print(); A c(0); //先创建对象 c = a; //这是拷贝赋值 //编译器默认浅拷贝 //需要自定义拷贝赋值--深拷贝 //system("pause"); return 0; }
全部默认参数或无参构造函数创建对象时注意的问题
#include <iostream> class A { public: A(int i=10){ m_pi = new int(i); ii = i; std::cout << i << std::endl; } void print(void) { std::cout << ii << std::endl; } private: int* m_pi; int ii; }; int main() { A a(100); A b();//注意:这个是不会创建对象的;编译器把这句当作函数的声明 A c; //这个会创建对象的 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
2019-08-11 python-pillow图像处理