析构函数是与构造函数对立的函数。
构造函数 | 析构函数 |
创建对象时手动调用 | 当对象销毁时,自动调用 |
函数名称与类名相同 | 函数名称是~类名 |
构造函数可以重载 | 析构函数没有参数,不能重载 |
用于创建对象时并初始化 | 用于销毁对象时释放资源 |
“有构造函数返回值” | 没有返回值 |
3.4.3的代码优化为:
#include?<iostream>
#include?<string.h>
using namespace std;
class Dog
{
private:
char *name;
public:
Dog(char *n)
{
????????name?= new char[20];
strcpy(name,n);
}
Dog(Dog?&d)
{
???????name?= new char[20];
strcpy(name,d.name);
}
void show_name()
{
????????cout?<<?name?<<?endl;
}
//?析构函数,对象销毁时自动调用(花括号结束对象销毁)
~Dog()
{
????????cout?<< "我被调用了" <<?endl;
????????delete?[]name;
????????name?=?NULL;
}
};
int main()
{
char?arr[20] = "旺财";
Dog d1(arr);
Dog d2(d1); //?拷贝构造函数
strcpy(arr,"大黄");
????d1.show_name(); //?旺财
????d2.show_name(); //?旺财
return 0;
}
匿名空间:默认空间
名字空间:用户命名
就近原则;
#include?<iostream>
using namespace std;//匿名空间
int?a?= 2;
namespace?my_space//名字空间
{
int?a?= 3;
int?b?= 4;
}
using namespace my_space;//使用名字空间
int main()
{
int?a?= 1;
????std::cout?<<?a?<<?std::endl; //?1?就近原则
????std::cout?<< ::a?<<?std::endl; //?::是匿名名字空间。2,匿名空间为空会报错
????cout?<<?my_space::a?<<?endl; //?3?名字空间
????cout?<<?b?<<?endl; //?4若没有其他匿名或就近,则直接调用唯一空间变量若有其他空间变量,则需要指定空间
return 0;
}
#include?<iostream>
using namespace?std;
class Demo
{
public:
Demo();//构造函数类内声明
void test(string?str);//类内声明函数
};
//类外实现
Demo::Demo()
{
????cout<<"创建了一个对象"<<endl;
}
//类外实现
void Demo::test(string?str)
{
????cout<<str<<endl;
}
int main()
{
????Demo?d;//创建一个类的对象
????d.test("helloworld?");//调用类内成员函数
return 0;
}
this指针是一个特殊指针,指向当前类对象的首地址。
成员函数(包括构造函数与析构函数)都有this指针。因此this指针只能在类内使用。实际上this指针指向的就是当前运行的成员函数所绑定的对象。
#include?<iostream>
#include?<string.h>
using namespace std;
class Test
{
private:
string?name;
public:
Test(string?name):name(name){}
void show_this()
{
????????cout?<< this <<?endl; //?0x61fe84
}
};
int main()
{
Test t1("小明");
????cout?<< &t1?<<?endl; //?0x61fe84
????t1.show_this();
????Test?*t2?= new Test("张三");
????cout?<<?t2?<<?endl;
????t2->show_this(); //?0x1327a8
delete?t2;
????t2?=?NULL;
return 0;
}
指针地址一致
#include?<iostream>
#include?<string.h>
using namespace std;
class Test
{
private:
string?name;
public:
Test(string?name):name(name){}
void func1()
{
????????cout?<< "哈哈哈哈" <<?endl;
}
void func2()
{
//?类内调用成员时,编译器自动添加this指针。
????????cout?<< this->name?<<?endl;
this->func1();
}
};
int main()
{
????Test?*t2?= new Test("张三");
????cout?<<?t2?<<?endl;
????t2->func2();
delete?t2;
????t2?=?NULL;
return 0;
}
#include?<iostream>
using namespace std;
class Test
{
private:
string?name;
public:
Test(string?name)
{
this->name?=?name;//指示当前类的成员对象
}
void test_this()
{
????????cout?<< this <<?endl; //?0x61fe84
}
};
int main()
{
Test t1("小明");
????cout?<< &t1?<<?endl; //?0x61fe84
????t1.test_this();
????Test?*t2?= new Test("张三");
????cout?<<?t2?<<?endl; //?0xf726a8
????t2->test_this();
return 0;
}
#include?<iostream>
using namespace std;
class Test
{
private:
string?name;
public:
Test(string?name)
{
this->name?=?name;
}
void func1()
{
????????cout?<< "哈哈哈哈哈" <<endl;
}
void func2()
{
//????????cout?<<?this->name?<<?endl;?//?this指针编译器帮我们自动添加
//????????this->func1();
}
};
int main()
{
Test t1("小明");
????t1.func2();
return 0;
}
支持链式调用的成员函数特点:
#include?<iostream>
#include?<string.h>
using namespace std;
class Test
{
private:
int?val?= 0;
public:
????Test?&add(int?i)
{
????????val?+=?i; //?val?=?val?+?i;
return *this;//返回类对象
}
int get_val()
{
return?val;
}
};
int main()
{
Test?t1;
????t1.add(1);
????t1.add(2);
????t1.add(100);
????cout?<<?t1.get_val() <<?endl;
//?链式调用
Test?t2;
????cout?<<?t2.add(2).add(21).add(300).add(221).get_val() <<?endl;
return 0;
}
使用static修饰局部变量,这样的变量就是静态局部变量。
静态局部变量在第一次调用的时候创建,指导程序结束后销毁,同一个类的所有对象共用这一份静态局部变量。
#include?<iostream>
using namespace?std;
class Test
{
public:
void func()
{
int?a?= 1;
static int?b?= 1;
????????cout?<< "a=" << ++a?<< "?" << &a?<<?endl;
????????cout?<< "b=" << ++b?<< "?" << &b?<<?endl;
}
};
int main()
{
????Test?t1;
????t1.func(); //?2,2
????t1.func();
????Test?t2;
????t2.func();
????Test?t3;
????t3.func();
return 0;
}
注意:a可能会在同一个内存地址,反复销毁创建。
使用static修饰成员变量,这样的变量就是静态成员变量。
静态成员变量需要在类内声明,类外初始化。
一个类的所有对象共用一份静态成员变量,虽然静态成员变量可以使用对象调用,但是更建议直接使用类名调用。所以静态成员变量可以脱离对象使用(非静态成员无法直接通过类名调用),在程序开始运行时就开辟内存空间,直到程序运行结束销毁。
更推荐使用类名直接进行调用。代码的可读性更好
#include?<iostream>
using namespace?std;
class Test
{
public:
int?a?= 1;
//????static?int?b?=?2;?//?错误,静态成员变量需要类内声明,类外初始化
static int?b;
};
int?Test::b?= 2;
int main()
{
????cout?<<?Test::b?<< "?" << &Test::b?<<?endl;//静态成员变量可以通过类名直接调用,脱离对象使用。
????Test?t1;
????cout?<<?t1.a++ << "?" << &t1.a?<<?endl;
????cout?<<?t1.b++ << "?" << &t1.b?<<?endl;
????cout?<<?t1.a++ << "?" << &t1.a?<<?endl;
????cout?<<?t1.b++ << "?" << &t1.b?<<?endl;
????cout?<< "----------------" <<?endl;
????Test?t2;
????cout?<<?t2.a++ << "?" << &t2.a?<<?endl;
????cout?<<?t2.b++ << "?" << &t2.b?<<?endl;
????cout?<<?Test::b?<< "?" << &Test::b?<<?endl;
return 0;
}
使用static修饰成员函数,这样的函数就是静态成员函数。
与静态成员变量相似的有:
静态成员函数没有this指针,不能在静态成员函数中调用同类中的其他非静态成员函数,但是静态成员函数,可以调用静态成员函数。
#include?<iostream>
using namespace?std;
class Test
{
public:
void func0()
{
func1();
????????cout?<<"非静态成员函数0"<<endl;
}
static void func1()
{
????????cout?<<"静态成员函数1"<<endl;
}
static void func2()
{
//????????func0();//静态成员函数没有this指针,不能在静态成员函数中调用同类中的其他非静态成员函数,
func1();//但是静态成员函数,可以调用静态成员函数。
????????cout?<<"静态成员函数2"<<endl;
}
};
int main()
{
Test::func1();
????Test?t1;
????t1.func0();
????t1.func1();
????t1.func2();
return 0;
}
【思考】我如果想在静态成员函数里,调用非静态成员函数,应该怎么做?
#include?<iostream>
using?namespace?std;
class?Test
{
public:
????void?func0()
????{
????????func1();
????????cout?<<"非静态成员函数0"<<endl;
????}
????static?void?func1()
????{
????????cout?<<"静态成员函数1"<<endl;
????}
????static?void?func2(Test?&t)
????{
????????t.func0();
//静态成员函数没有this指针,不能在静态成员函数中调用同类中的其他非静态成员函数,
????????func1();//但是静态成员函数,可以调用静态成员函数。
????????cout?<<"静态成员函数2"<<endl;
????}
};
int?main()
{
????Test::func1();
????Test?t1;
????t1.func0();
????t1.func1();
????t1.func2(t1);
????return?0;
}
const修饰的成员函数,表示常成员函数,特性如下:
建议只要成员函数不修改变量,就使用const修饰,例如:get、show等函数。
#include?<iostream>
using?namespace?std;
class?Demo
{
private:
????int?a;
public:
????Demo(int?a)
????{
????????this->a?=?a;
????}
????void?func()
????{
????????cout?<<?"哈哈哈哈"?<<?endl;
????}
????int?get_demo()?const
????{
????????return?a;
????}
????void?test()?const
????{
//????????a++;?//?错误?const修饰的成员函数,不能修改成员变量
????????cout?<<?a?<<?endl;
//????????func();?错误?const修饰的成员函数,不能调用非const?修饰的成员函数,只能调用const修饰的成员函数
????????cout?<<?get_demo()?<<?endl;
????????int?i?=?1;
????????i++;
????????cout?<<?i?<<?endl;
????}
};
int?main()
{
????Demo?demo(1);
????cout?<<?demo.get_demo()?<<?endl;
????demo.func();
????demo.test();
????return?0;
}
const修饰的对象被称为常量对象,这种对象的成员变量值无法被修改,也无法调用非const的成员函数。
#include?<iostream>
using?namespace?std;
class?Demo
{
private:
????int?a;
public:
????int?b?=?20;
????Demo(int?a)
????{
????????this->a?=?a;
????}
????void?func()
????{
????????cout?<<?"哈哈哈哈"?<<?endl;
????}
????int?get_demo()?const
????{
????????return?a;
????}
????void?test()?const
????{
//????????a++;?//?错误?const修饰的成员函数,不能修改成员变量
????????cout?<<?a?<<?endl;
//????????func();?错误?const修饰的成员函数,不能调用非const?修饰的成员函数
????????cout?<<?get_demo()?<<?endl;
????????int?i?=?1;
????????i++;
????????cout?<<?i?<<?endl;
????}
};
int?main()
{
//????const?Demo?demo(1);
????Demo?const?demo(1);?//?两种初始化方式,等效于上一行
????cout?<<demo.get_demo()?<<?endl;
//????demo.func();?//?错误?const修饰的对象,无法调用非const的成员函数
????demo.test();
//????demo.b++;?//?错误const修饰的对象,无法修改成员变量
????cout?<<?demo.b?<<?endl;?//?可以调用,但是不能修改
????return?0;
}
cosnt修饰的成员变量称为常成员变量,表示该成员变量的值无法被改变。
常成员变量有两种初始化的方式:
两种方式一块使用时,前者失效,后者为准。
#include?<iostream>
using?namespace?std;
class?Demo
{
private:
????const?int?a?=?1;
????const?int?b?=?2;
????const?int?c?=?3;//初始化赋值
public:
????Demo(int?a,int?b,int?c):a(a),b(b),c(c){}//构造初始化列表赋值
????void?show()
????{
????????cout?<<?a?<<?"?"?<<?b?<<?"?"?<<?c?<<?endl;
????}
????void?test()
????{
????????//????????a++;?//?错误,const修饰的成员变量,无法被改变
????????//????????b++;
????????//????????c++;
????????cout<<"const修饰的成员变量,无法被改变"<<endl;
????}
};
int?main()
{
????Demo?demo(10,20,30);
????demo.test();
????demo.show();
????return?0;
}
const修饰局部变量,表示该局部变量不可被修改。
这种方式常用于引用参数
#include?<iostream>
#include?<string.h>
using namespace?std;
class Demo
{
private:
const int?a?= 1;
const int?b?= 2;
const int?c?= 3;
public:
//?构造初始化列表
Demo(int?a,int?b,int?c):a(a),b(b),c(c){}
void show()
{
????????cout?<<?a?<< "?" <<?b?<<"?"<<?c?<<?endl;
}
void test()
{
const int?d?= 10;
//????????d?=?20;???//?错误,const修饰的局部变量,无法被修改。
????????cout?<<?d?<<?endl;
}
};
int main()
{
????Demo?d(10,20,30);
????d.show(); //?以列表为准
????d.test();
return 0;
}