构造函数是与同类名的特殊成员函数,主要用来初始化对象的数据成员。
构造函数的特点:
#include <iostream>
using namespace std;
class student{
private:
string m_name;
int m_age;
int m_num;
public:
student(const string &name, int age, int num) {
m_name = name;
m_age = age;
m_num = num;
}
void Cout() {
cout << "my name is " << m_name << "," << "my age is " << m_age << endl;
}
};
int main(int argc, const char *argv[])
{
student s("xiaoming", 18, 1234);
s.Cout();
return 0;
}
缺省构造函数也称无参构造函数,但其未必真的没有任何参数,为一个有参构造函数的每个参数都提供一个缺省值,同样可以达到无参构造函数的效果。
注意:
#include <iostream>
using namespace std;
class A{
public:
A(void) {
cout << "这是A的构造函数" << endl;
m_t = 0;
}
public:
int m_t;
};
class B{
public:
int m_j;//基本类型成员变量
A m_a;//类类型成员变量(成员子对象)
};
int main(int argc, const char *argv[])
{
B b;//调用成员对象m_a的无参构造函数 调用B的缺省构造函数
cout << b.m_j << endl;
cout << b.m_a.m_t << endl;
return 0;
}
#include <iostream>
using namespace std;
class Desk{
private:
int length, weight, hight, wide;
public:
Desk(int l, int w, int h, int ww);
void put() {
cout << length << weight << hight << wide << endl;
}
Desk() {
cout << "Desk(void)" << endl;
length = 0;
weight = 0;
hight = 0;
wide = 0;
}
};
Desk::Desk(int l, int w, int h, int ww) {
cout << "construct" << endl;
length = l;
weight = w;
hight = h;
wide = ww;
}
int main(int argc, const char *argv[])
{
Desk s(1,2,3,4);
s.put();
Desk();
return 0;
}
将其他类型转换为当前类类型需要借助转换构造函数,转换构造函数只有一个参数。
#include <iostream>
#include <cstring>
using namespace std;
class test {
private:
int m_i;
string m_c;
public:
test(void) {
cout << "test(void)" <<endl;
m_i = 0;
}
explicit test(int n) {
cout << "test(int)" << endl;//类型转换构造函数
m_i = n;
}
explicit test(const char *str) {
cout << "test(char *)" << endl;
m_i = strlen(str);
m_c = str;
cout << m_c << endl;
}
void printf() {
cout << m_i << endl;
}
};
int main(int argc, const char *argv[])
{
test a1;
a1.printf();
test a = test(2);//编译器会找参数类型为int的构造参数
a.printf();
test b = test("abc");//编译器会找参数类型为char *的构造参数
b.printf();
return 0;
}
explicit关键字,就是告诉编译器需要类型转换时,强制要求写成如下形式:
test a = test(2);
test a = 2;//会报错
用一个已定义的对象构造同类型的副本对象,将调用该类的拷贝构造构造函数
class A{
A(const A &that){//拷贝构造函数,注意参数必须是常引用
...
}
};
A a;
A b(a);//调用拷贝构造
A c = a;//调用拷贝构造
#include <iostream>
using namespace std;
class A{
public:
int m_t;
A(int date = 0) {
cout << "A(int)" << endl;
m_t = date;
}
A(const A& that) {//拷贝构造函数
cout << "A(const A&)" << endl;
m_t = that.m_t;
}
};
int main(int argc, const char *argv[])
{
A a;
A b(a);//编译器会调用拷贝构造函数
A c = a;//调用拷贝构造函数
return 0;
}
class User {
string m_name;//调用string类的拷贝构造函数
int m_age;//按字节复制
};
#include <iostream>
using namespace std;
class A{
public:
int m_t;
A(int date = 0) {
cout << "A(int)" << endl;
m_t = date;
}
A(const A& that) {//拷贝构造函数
cout << "A(const A&)" << endl;
m_t = that.m_t;
}
};
class B{
public:
A m_a;
};
int main(int argc, const char *argv[])
{
#if 0
A a;
A b(a);//编译器会调用拷贝构造函数
A c = a;//调用拷贝构造函数
B d;
#endif
B b;
b.m_a.m_t = 98;
B b1;
b1 = b;
cout << b1.m_a.m_t << endl;//调用B的缺拷贝构造函数,由于有成员对象m_t,所以会调用A的拷贝构造函数
return 0;
}
构造函数对数据成员进行初始化还可以通过成员初始化列表的方式完成。语法格式为:
构造函数名(参数表):成员1(初始值参数),成员2(初始值参数){
}
例如:
#include <iostream>
using namespace std;
class Desk{
private:
int length, weight, hight, wide;
public:
Desk(int l, int w, int h, int ww);
void put() {
cout << length << weight << hight << wide << endl;
}
Desk() {
cout << "Desk(void)" << endl;
}
};
Desk::Desk(int l, int w, int h, int ww) :length(l), weight(w), hight(h), wide(ww){
cout << "construct" << endl;
length = l;
weight = w;
hight = h;
wide = ww;
}
int main(int argc, const char *argv[])
{
Desk s(1,2,3,4);
s.put();
Desk s2;
return 0;
}
一般而言,使用初始化列表和在构造函数体对成员变量进行赋初值,两者区别不大,可以人员一种,但是下面几种场景必须使用初始化列表:
#include <iostream>
using namespace std;
class A{
private:
int date_t;
public:
A(int date) {
date_t = date;
cout << "A(int)" << endl;
}
};
class B {
private:
int m_t;
public:
B(void) :m_t(123){
cout << "B(void)" << endl;
cout << m_t << endl;
}
};
int main(int argc, const char *argv[])
{
B b;//一定会去构造成员对象m_t,未指定如何构造,系统去调用m_t的无参构造函数
return 0;
}
#include <iostream>
using namespace std;
int num = 100;
class A{
public:
int & m_t;
const int m_c;
/*
* error此种写法错误,无法const类型和引用类型无法这样初始化
A(void) {
m_t = num;
m_c = 100;
}
*/
A(void):m_t(num),m_c(num) {
cout << m_t << " " << m_c << endl;
}
};
int main(int argc, const char *argv[])
{
A a;
return 0;
}
类中成员变量按声明顺序依次被初始化,而与初始化中的顺序无关。
#include <iostream>
using namespace std;
class A{
public:
A(int a) {
cout << "A(construct)" << endl;
}
};
class B{
public:
B(int b) {
cout << "B(construct)" << endl;
}
};
class C{
private:
A m_a;
B m_b;
public:
C(int a, int b): m_b(b), m_a(a) {
}
};
int main(int argc, const char *argv[])
{
C c(2,3);
return 0;
}
#include <iostream>
using namespace std;
class stu{
private:
int age;
string name;
public:
stu(int m_age, string m_name) {//编译完成之后就是stu(stu *this, int m_age, string m_name);
age = m_age;
name = m_name;
}
void cprintf() {
cout << name << ":" << age << endl;
}
};
int main(int argc, const char *argv[])
{
stu zs(18,"zhangsan");//传递参数为(&zs,18,"zhangsan");
stu ls(21,"lisi");//传递参数为(&ls, 21, "lisi");
zs.cprintf();
ls.cprintf();
return 0;
}
this是一个用于标识对象自身的隐式指针,代表对象自身的地址。
在编译类成员函数时,C++编译器会自动将this指针添加到成员函数的参数表中。在用类的成员函数时,调用对象会把自己的地址通过this指针传递给成员函数。
需要显式使用this指针的常见场景:
#include <iostream>
using namespace std;
class Count{
private:
int count;
public:
Count(int count = 0) {
this->count = count;
}
Count &add(void) {
++count;
return *this;
}
void printf() {
cout << count << endl;
}
void destroy() {
cout << "this:" << this << endl;
delete this;
}
};
int main(int argc, const char *argv[])
{
Count cnt;
cnt.printf();
cnt.add().add().add();
cnt.printf();
Count *pnt = new Count;
pnt->printf();
pnt->add();
pnt->printf();
pnt->destroy();
cout << pnt << endl;
return 0;
}
在C++中,为了禁止成员函数修改成员数据的值,可以将它设置为常成员函数。设置方法就是在函数体之前加上const关键字。
class X{
void func(参数1, 参数2, ...) const {
}
};
#include <iostream>
using namespace std;
class A {
private:
int num,age;
string name;
public:
A(int num, const string &name, int age) {
this->age = age;
this->num = num;
this->name = name;
}
void printf(void) const{
cout << "i am " << name << "i am age is " << age << "num:" << num << endl;
}
};
int main(int argc, const char *argv[])
{
A a(8499, "zhangsan", 21);
a.printf();
return 0;
}
常函数的使用注意事项:
#include <iostream>
using namespace std;
class A{
private:
int n,m;
public:
A(int nn = 1, int mm = 2):n(nn),m(mm) {};
void func(void) {
cout << "this is 11" << endl;
}
void bar(void) const{
cout << "this is 15"<< endl;
}
void func(void) const{
cout << "this is 19" << endl;
}
};
int main(int argc, const char *argv[])
{
A a;
a.func();
a.bar();
const A b;
b.func();
return 0;
}
析构函数是与类同名的另一个特殊成员函数,作用与构造函数相反,用于对象生存期结束时,完成对象的清理工作。析构函数的名称是:~类名
class X{
public:
X() {//构造函数
}
~X() {//析构函数
}
};
析构函数的特点:
#include <iostream>
using namespace std;
class Integer{
private:
int *m;
public:
Integer(int i = 0) {
m = new int(i);
}
~Integer(void) {
cout << "析构函数" << endl;
delete m;
}
void printf(void) {
cout << *m << endl;
}
};
int main(int argc, const char *argv[])
{
if(1) {
Integer a(100);
a.printf();
cout << "test1" << endl;
Integer *pi = new Integer(200);
pi->printf();
delete pi;
cout << "test2" << endl;
}
cout << "test3" << endl;
return 0;
}
如果类没有显式定义析构函数,那么编译器会为其提供一个缺省析构函数,缺省析构的函数体为空,在析构函数体执行完毕后,类中的成员会被自动销毁。
#include <iostream>
using namespace std;
class A{
public:
A(void) {
cout << "A(void)" << endl;
}
~A(void) {
cout << "~A(void)" << endl;
}
};
class B{
public:
B(void) {
cout << "B(void)" << endl;
}
~B(void) {
cout << "~B(void)" << endl;
}
A a;
};
int main(int argc, const char *argv[])
{
B b;
return 0;
}
对象创建
对象销毁