丹阳市网站制作,培训网站建设方案模板,php本地建站工具,自己做网站如何赚钱文章目录 前言一、this指针二、构造和析构三、深拷贝浅拷贝浅拷贝深拷贝 编程实践 前言
什么是OOP思想#xff1f;
OOP语言的四大特征#xff1a; 抽象#xff0c;封装/隐藏#xff0c;继承#xff0c;多态
一、this指针
this指针》类》很多对象 一套成员方法是如何处… 文章目录 前言一、this指针二、构造和析构三、深拷贝浅拷贝浅拷贝深拷贝 编程实践 前言
什么是OOP思想
OOP语言的四大特征 抽象封装/隐藏继承多态
一、this指针
this指针》类》很多对象 一套成员方法是如何处理多个对象的具体而言
成员方法show() 怎么知道处理哪个对象的信息? 答具体对象的this指针。 成员方法init (namepriceamount) 怎么知道把信息初始化给哪一个对象的 答具体对象的this指针。 类的成员方法一经编译所有的方法参数都会加一个this指针接收调用该方法的对象的地址。
当有一实例化对象good1good1调用成员方法show时编译器会自动加入参数 show(good1); 当有一实例化对象good2good2调用成员方法init时编译器会自动加入参数 init(good2namepriceamount);
二、构造和析构
OOP实现顺序栈。
要想实现一个类共有一般放成员方法私有一般放成员变量。 因为顺序栈要实现内存的扩充所以需要使用数组指针。
class SeqStack
{
public:// 构造函数 SeqStack(int size 10){cout SeqStack() Ptr this endl;_pstack new int[size];_top -1;_size size;}// 自定义的拷贝构造函数 《 对象的浅拷贝现在有问题了SeqStack(const SeqStack src){cout SeqStack(const SeqStack src) endl;_pstack new int[src._size];for (int i 0; i src._top; i){_pstack[i] src._pstack[i];}_top src._top;_size src._size;}// 析构函数~SeqStack() {cout this ~SeqStack() endl;delete[]_pstack;_pstack nullptr;}void push(int val){if (full()) resize(); // resize不想要用户调他_top;_pstack[top] val;}void pop(){if (empty()) return;--_top;}int top() { return _pstack[_top]; }bool empty() { return _top -1; }bool full() { return _top _size - 1; }private:int *_pstack; // 动态开辟数组存储顺序栈的元素int _top; // 指向栈顶元素的位置int _size; // 数组扩容的总大小void resize(){int *ptmp new int[_size * 2];for (int i 0; i _size; i){ptmp[i] _pstack[i];} // memcpy(ptmp, _pstack, sizeof(int)*_size); reallocdelete[]_pstack;_pstack ptmp;_size * 2;}
}析构函数如果需要释放一个指向数组的指针需要中括号[]
delete[]_pstack;调用
SeqStack ps1; // .data段开辟内存程序结束才进行析构
int main()
{SeqStack *ps2 new SeqStack(60); //heap段开辟内存ps2-push(70);ps2-pop();cout ps2-top() endl;delete ps2; // 1、在stack段开辟内存 2、调用构造SeqStack ps3;
}调用总结 1、全局对象 全局对象定义时进行构造程序结束才析构。 2、heap段对象 new时1、malloc内存开辟2、构造函数。 delete时1、析构函数2、free(ps2) 3、stack段对象 定义时构造出作用域析构。
三、深拷贝浅拷贝
浅拷贝
内存的拷贝
int main()
{SeqStack s1(10);SeqStack s2 s1; // #1SeqStack s3(s1); // #2// #1和#2都是调用拷贝构造// #1相当于 s2.operator(s1)// void operator(const SeqStack src)
}什么时候不能浅拷贝 对象中的成员变量指针指向了对象外的资源。所以在浅拷贝时两个对象的指针就指向了同一块资源。
深拷贝
当对象占用外部资源时使用深拷贝使得其各自占有各自的资源。
class SeqStack
{
public:// 构造函数 SeqStack(int size 10) {}// 自定义的拷贝构造函数 《 对象的浅拷贝现在有问题了SeqStack(const SeqStack src){cout SeqStack(const SeqStack src) endl;_pstack new int[src._size];for (int i 0; i src._top; i){_pstack[i] src._pstack[i];}_top src._top;_size src._size;}void operator (const SeqStack src){if(this src) return; // 防止自赋值delele[] _pstack; // 重要需要先释放当前对象的内存_pstack new int[src.size];for (int i 0; i src._top; i){_pstack[i] src._pstack[i];}_top src._top;_size src._size;}// 析构函数~SeqStack() {}void push(int val){}void pop(){}int top() {}bool empty() {}bool full() {}
private:int *_pstack; // 动态开辟数组存储顺序栈的元素int _top; // 指向栈顶元素的位置int _size; // 数组扩容的总大小void resize(){}
}注意 1、为什么拷贝时要使用for循环而不是直接
memcpy(ptmp, _pstack, sizeof(int)*_size);使用memcpy仍然是浅拷贝。 2、赋值构造函数时有一个释放当前对象的内存的操作。对应shared_ptr引l用计数在赋值的时候也会减1随即会加1 3、赋值构造函数应该防止s1 s1这种自赋值的情况。
编程实践
Mystring 注意 1、在普通构造函数中无论如何也要开辟一块内存。保证对象是有一个有效的对象。 2、普通构造函数的输入为const char *str因为在新版编译器中不让普通的指针指向常量字符串。
char *p hello world; // 不能修改*p
现在编译器都不让普通的指针指向常量字符串应该这么写
const char *p hello world;#include bits/stdc.husing namespace std;class String
{
public:String(const char *str nullptr) {if (str ! nullptr){m_data new char[strlen(str) 1]; // 加上/0strcpy(this-m_data, str);}else{m_data new char[1]; // new char;*m_data \0; // 0}}String(const String other){m_data new char[strlen(other.m_data) 1]; // 深拷贝strcpy(m_data, other.m_data);}~String(void) // 析构函数{delete[]m_data;m_data nullptr;}// String 是为了支持连续的operator赋值操作String operator(const String other) // 赋值重载函数{if (this other){return *this; // str1}delete[]m_data; // 析构m_data new char[strlen(other.m_data) 1];strcpy(m_data, other.m_data);return *this; // str1}
private:char *m_data; // 用于保存字符串
};
int main()
{// 调用带const char*参数的构造函数String str1;String str2(hello);String str3 world;// 调用拷贝构造函数String str4 str3;String str5(str3);// 调用赋值重载函数/*str1 str2str1.operator(str2) str1str3 str1*/str3 str1 str2;return 0;
}关于赋值重载函数中返回值为引用的情况 如果一个方法的返回值是一个引用那么它返回的是某个对象的引用而不是对象本身的副本。这意味着通过引用返回的值与原始对象共享同一块内存对返回值的修改将直接影响原始对象。 返回引用的方法有以下几个作用
避免对象的复制通过返回引用而不是副本可以避免在函数调用中进行大量的复制操作提高性能。特别是对于大型对象或复杂的数据结构避免复制可以节省时间和内存。允许链式操作返回引用可以使多个方法调用可以连接在一起形成链式操作。这种方法通常用于实现流畅的、易读的代码比如在输入/输出流中使用和运算符。允许对返回值进行修改返回引用允许在函数外部对返回值进行修改。这可以用于实现函数返回某个对象的引用以便可以直接修改该对象的状态。
需要注意的是返回引用时需要确保返回的引用在函数调用结束后仍然有效。一般来说应该返回指向静态、全局、或动态分配的对象的引用而不是指向局部对象的引用以避免出现悬垂引用dangling references的问题。
完整的Mystring重写见参考链接