this指针初探
this关键字
this指针
- 每个成员函数(包括构造和析构)都有一个this指针
- this指针指向调用对象,即可以通过this关键字访问当前对象的成员
访问成员变量
this -> 成员名;
访问成员函数
this -> 函数名();
this 在 C++ 是函数的隐含第一个参数。只能在函数内部使用
在函数的结尾声明为const: 函数内部不允许修改对象本身,调用本对象非const方法就会报错
大程序使用 new. 栈内存消毁问题。
初始运算符重载
- 运算符重载 重点- 难点
- 友元函数
- 一元运算符重载
- 重载 << 运算符 重点
- 类的自动转换和强制类型转换
运算符重载(Operator Overloading)
运算符重载就是“想法转换”,他的目标是简化函数调用的方式
- 把标准的函数使用方式,重新定义为自己认为的方式
所谓重载,就是赋予新的意义
运算符重载也是一个道理,同一个运算符可以有不同的功能
实际上,我们已经不知不觉中使用了运算符重载
- +号可以对不同类型(int,float等)的数据进行加法操作
- <<既是位移运算符,又可以配合cout向控制台输出数据
- C++本身已经对这些运算符进行了重载
那个是编译器的内部调用,程序员是不能使用那个语句的。
#include <iostream>
#include "Integer.h"
using namespace std;
void TestInteger();
int main()
{
//cout << "Hello world!" << endl;
TestInteger();
return 0;
}
void TestInteger()
{
Integer int1(1024), int2(2048), int3;
int3 = int1 + int2;
cout <<"int3 = int1 + int2的结果为:" << int3.IntValue() << endl;
}
```cpp .h
#ifndef INTEGER_H
#define INTEGER_H
//我们自己定义的整型类,将整型封装成类,以便实现面向对象的封装
class Integer
{
public:
Integer();
Integer(int value) : m_value(value){} //实现
//重载+运算符
Integer operator+(Integer other);
int IntValue(){return m_value;}
virtual ~Integer();
protected:
private:
int m_value; //实际的整型数字 m---是member的意思,私有成员
};
#endif // INTEGER_H
#include "Integer.h"
//调用默认构造时,会为私有m_value 赋一个默认值为0
Integer::Integer() : m_value(0)
{
//ctor
}
//重载+运算符
Integer Integer:: operator+(Integer other)
{
Integer result(this -> m_value + other.m_value); //用来返回的结果对象
return result;
}
Integer::~Integer()
{
//dtor
}
详解const
1.const修饰变量
num2 不能再去赋值–const
2.const能不能修饰指针变量?
1.只有一个const时,如果const位于的左侧 : 表示指针所指的数据是常量,不能通过该指针修改实际数据,指针本身是变量,可以指向其他内存单元
2.只有一个const时,如果const位于的右侧:表示指针本身是常量,不能指向其他内存单元:所指向的数据可以修改
3.如果有两个const位于*的左右两侧,表示指针和指针所指向的数据都不能修改
3.const 修饰函数参数
都变成const就可以了调用了
void buy() const{}
4.const修饰返回值
const 在返回一个对象引用时更有效率。
强调:使用const修饰引用类型的一个常见的原因是:提高效率
如果函数要返回局部对象,就应该直接返回这个对象,而不要返回对象的引用。
局部对象会在使用后进行销毁
在可以返回对象,也可以可返回引用时,就应该首选引用,因为效率高
5.const修饰函数-说明函数不会修改成员变量的值
重载赋值符号
直接返回*this ,而不返回引用对象,更节省时间
二元运算符:+ - * / 取模
友元函数
因为友元函数没有this指针,则参数要有三种情况:
要访问非static成员时,需要对象做参数;
要访问static成员或全局变量时,则不需要对象做参数;
如果做参数的对象是全局对象,则不需要对象做参数.
可以直接调用友元函数,不需要通过对象或指针
复制构造函数
需求说明:
- 自定义String(字符串)类,以简化字符串的操作
如果字符串长度为0的话,那么实例化为1的空间。空间取值为“/0”
头文件名字为String.h容易和string.h混不清楚。
改为myString
private:
int m_length; //字符串的实际长度,不包括/0
char *m_value; //实际存储字符的字符数组 在C++中一旦定义数组,就要给他长度。char m_value[]; 要给长度。
String::String() : m_length(0)
{
//char * str = ''; //长度为0,但是实际的字符数组中会存在唯一的元素:\0
this -> m_value = new char[1]; //不能是0----- 定义长度为0的时候,分配一个空间为1
this -> m_value ='\0';
friend ostream & operator <<(ostream & out,const String &str)
{
out << str.m_value;
return 0
}
*文件名;
*描 述;
*作 者;
*时 间;
*版 权;
*/
#include “MyString.h”
String::String() : m_length(0)
{
//char * str = ‘’; //长度为0,但是实际的字符数组中会存在唯一的元素:\0
this -> m_value = new char[1]; //不能是0----- 定义长度为0的时候,分配一个空间为1
this -> m_value =’\0’;
friend ostream & operator <<(ostream & out,const String &str)
{
out << str.m_value;
return 0
}
//ctor
}
String::~String()
{
//析构时,释放字符数组所指向的空间—如果不销毁的话,会造成内存泄露
delete[] m_value;
//字符串被销毁的时候,长度为0。但是指针所指的空间还存在。
//所以要销毁空间
//dtor
}
利用构造函数实现字符串的复制: 好处是在赋值的时候很方便
#ifndef STRING_H
#define STRING_H
#include<iostream>
#include <string >
using namespace std;
//自定义的字符串包装类
class String
{
public:
String();
String(char * str);
virtual ~String();
friend ostream & operator <<(ostream & out,const String &str);
protected:
private:
int m_length; //字符串的实际长度,不包括/0
char *m_value; //实际存储字符的字符数组 在C++中一旦定义数组,就要给他长度。char m_value[]; 要给长度。
};
#endif // STRING_H
#include <iostream>
#include <string.h>
#include "MyString.h"
using namespace std;
String::String() : m_length(0)
//char * str = ''; //长度为0,但是实际的字符数组中会存在唯一的元素:\0
{
this -> m_value = new char[1]; //不能是0----- 定义长度为0的时候,分配一个空间为1
this -> m_value[0] ='\0';
}
String::String(char *str)
{
//将传入的字符串str的值赋值给当前对象中的m_value
if(NULL == str){
this -> m_value = new char[1]; //不能是0----- 定义长度为0的时候,分配一个空间为1
this -> m_value[0] ='\0';
return;
}
m_length = strlen(str); //获得要复制字符串的长度
m_value = new char[m_length +1];// 为/0留出一个空间
strcpy(m_value, str);
}
ostream & operator <<(ostream & out,const String &str)
{
out << str.m_value << "\n";
out << "str的长度:" << str.m_length;
return out;
}
String::~String()
{
//析构时,释放字符数组所指向的空间---如果不销毁的话,会造成内存泄露
delete[] m_value;
//字符串被销毁的时候,长度为0。但是指针所指的空间还存在。
//所以要销毁空间
//dtor
}
#include <iostream>
#include <string.h>
#include "MyString.h"
using namespace std;
String::String() : m_length(0)
//char * str = ''; //长度为0,但是实际的字符数组中会存在唯一的元素:\0
{
this -> m_value = new char[1]; //不能是0----- 定义长度为0的时候,分配一个空间为1
this -> m_value[0] ='\0';
}
String::String(char *str)
{
//将传入的字符串str的值赋值给当前对象中的m_value
if(NULL == str){
this -> m_value = new char[1]; //不能是0----- 定义长度为0的时候,分配一个空间为1
this -> m_value[0] ='\0';
return;
}
m_length = strlen(str); //获得要复制字符串的长度
m_value = new char[m_length +1];// 为/0留出一个空间
strcpy(m_value, str);
}
ostream & operator <<(ostream & out,const String &str)
{
out << str.m_value << "\n";
out << "str的长度:" << str.m_length;
return out;
}
String::~String()
{
//析构时,释放字符数组所指向的空间---如果不销毁的话,会造成内存泄露
delete[] m_value;
//字符串被销毁的时候,长度为0。但是指针所指的空间还存在。
//所以要销毁空间
//dtor
}
利用构造可以直接通过你输入的参数判断,使用的是那个构造函数。可以直接出来结果
小结
- 运算符重载就是“想法转换”,他的目标是简化函数调用的方式
把标准的函数使用方法,重新定义为自己认为的方式 - 友元函数
流运算符<< 、>>,类型转换运算符不能定义为类的成员函数,只能是友元函数
二元运算符在运算符重载时,一般声明为友元函数 - 赋值/拷贝构造函数
对于简单的类,默认拷贝构造函数一般是够用的,没必要再显式定义一个功能类似的拷贝构造函数
当类拥有其他资源时,如动态分配的内存,打开的文件,指向其他数据的指针,网络连接等,默认拷贝构造函数就不能拷贝这些资源,必须显示定义拷贝构造函数,以完整地拷贝对象的所有数据。