析构函数
析构函数是与构造函数对立的函数。
构造函数 | 析构函数 |
创建对象时手动调用 | 当对象销毁时,自动调用 |
函数名称与类名相同 | 函数名称是~类名 |
构造函数可以重载 | 析构函数没有参数,不能重载 |
用于创建对象时并初始化 | 用于销毁对象时释放资源 |
“有构造函数返回值” | 没有返回值 |
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;
}
- 作用域限定符“::”
匿名空间:默认空间
名字空间:用户命名
就近原则;
1 名字空间(熟悉)
#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;
}
2 类内声明,类外定义,主函数内调用
#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指针
1 概念
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;
}
指针地址一致
- 类中成员的调用都依赖this指针,通常由编译器自动添加。
#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;
}
2 功能
- 区分重名的局部变量与成员变量
#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;
}
- 类中成员的调用都依赖于this指针,通常由编译器自动添加。
#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;
}
- 链式调用
支持链式调用的成员函数特点:
- 返回值时当前类的引用。
- return 后面是*this
#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 关键字
1 静态局部变量
使用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可能会在同一个内存地址,反复销毁创建。
2 静态成员变量
使用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;
}
3 静态成员函数
使用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;
}
【思考】我如果想在静态成员函数里,调用非静态成员函数,应该怎么做?
- 可以通过参数将对象传进来,因为静态成员函数没有this指针,例如:
#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关键字
1 const修饰成员函数
const修饰的成员函数,表示常成员函数,特性如下:
- 可以调用成员变量,但是不能修改成员变量的值。
- 不能调用非const修饰的成员函数,哪怕这个函数并没有修改成员变量。(const修饰的成员函数,不能调用非const 修饰的成员函数,只能调用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;
}
2 const修饰对象
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;
}
3 const修饰成员变量
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;
}
4 const修饰局部变量
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;
}