首页 > 系统相关 >C++第十二章:动态内存与类+断点错误

C++第十二章:动态内存与类+断点错误

时间:2022-11-08 22:45:39浏览次数:53  
标签:const 动态内存 stringbad C++ char len str operator 断点

前言

C++类是针对对象的一种控制方法,可以看作各种函数与变量的管理方。类同样也会消耗内存,而且类一旦被创建,意味着相关成员会大量调用,此时内存的管理极其重要,常采用delete和new嵌入类的成员函数中对变量进行操作。

代码

#pragma once
#include <iostream>
using namespace std;

class stringbad
{
public:
	stringbad();
	stringbad(const char *s);//放置new
	~stringbad();//用于放置delete

	int length() const { return this->len; };

	//考虑复制构造函数和复制运算符两种函数,适配不同编译器
	stringbad& operator=(const stringbad& s);
	stringbad(const stringbad& s);

	stringbad& operator=(const char* s);
	const char &operator[] (int i) const;
	friend bool operator<(const stringbad& s1, const stringbad& s2);
	friend bool operator>(const stringbad& s1, const stringbad& s2);
	friend bool operator==(const stringbad& s1, const stringbad& s2);
	friend ostream& operator<<(ostream& os, const stringbad& s);
	friend istream& operator>>(istream& is, stringbad& s);
	static int HowMany();//静态区域的成员函数,只能指定访问,不能this.
	static const int CINLIM = 80;
private:
	char* str;//字符串指针
	int len;//字符串长度
	static int num_strings;//字符串数量,静态局部变量
	
};
#include "stringbad.h"
#include <cstring>

void callme1(stringbad& s);//引用传递
void callme2(stringbad s);//引用传递

int stringbad::num_strings = 0;//将static变量初始化,但是不可以在类声明时初始化静态类成员!所有的类成员共享~
int stringbad::HowMany()
{
	return stringbad::num_strings;
}

//构造函数
stringbad::stringbad(const stringbad& s)
{
	this->str = new char[s.len + 1];
	strcpy_s(this->str, s.len + 1, s.str);//拷贝字符串
	this->len = s.len;//计算长度
	this->num_strings++;
}

stringbad::stringbad()
{
	this->len = 4;
	str = new char[1];
	str[0] = '\0';
	num_strings++;
}

stringbad::stringbad(const char* s)//放置new
{
	this->str = new char[strlen(s) + 1];
	strcpy_s(str, strlen(s)+1,s);//拷贝字符串
	this->len = strlen(s)+1;//计算长度
	this->num_strings++;
}



bool operator<(const stringbad& s1, const stringbad& s2)
{
	return (std::strcmp(s1.str, s2.str) < 0);
}

bool operator>(const stringbad& s1, const stringbad& s2)
{
	return (std::strcmp(s2.str, s1.str) < 0);
}

bool operator==(const stringbad& s1, const stringbad& s2)
{
	return (std::strcmp(s2.str, s1.str) == 0);
}

ostream& operator<<(ostream& os, const stringbad& s)
{
	os << "string : " << s.str << endl;
	return os;
}

istream& operator>>(istream& is, stringbad& s)
{
	char temp[stringbad::CINLIM];
	is.get(temp, stringbad::CINLIM);

	s = temp;//赋值构造函数

	//消耗缓存的字符
	while (is && is.get() != '\n')
		continue;

	return is;
}

stringbad& stringbad::operator=(const char* s)
{
	delete[] str;
	len = std::strlen(s);
	this->str = new char[strlen(s) + 1];
	strcpy_s(str, len + 1, s);
	return *this;
}

//析构函数
stringbad::~stringbad()//用于放置delete
{
	cout << num_strings << endl;
	--num_strings;
	delete []str;//释放空间
}

/*************************成员函数**************************/

//赋值构造函数
stringbad& stringbad::operator=(const stringbad& s)
{

	//那如果自己赋值给自己呢?就不可以delete,直接返回自己就行
	if (this == &s)
	{
		return *this;
	}

	delete[]str;//去除调用本身的内存空间,因为下面还会new

	this->str = new char[s.len + 1];
	strcpy_s(this->str, s.len + 1, s.str);//拷贝字符串
	this->len = s.len;//计算长度
	this->num_strings++;

	return *this;
}

const char& stringbad::operator[] (int i) const
{
	return str[i];
}



int main(void)
{
	//stringbad headline1("HELLOWORLD");
	//stringbad headline2("GOOD MORNING");
	//stringbad headline3("SPORTS");

	//cout << headline1 << endl;
	//callme1(headline1);
	//callme2(headline1);//按值传递出问题了,可以自己再编写一个复制构造函数

	cout << "HI! What's your name? " << endl;
	stringbad name;
	cin >> name;
	cout << name << endl;

	//stringbad name1("zhangwei");
	//cout << name1;
	return 0;
}

void callme1(stringbad& s)//引用传递-原对象还在
{
	cout << "string passed by reference: " << s << endl;
}

void callme2(stringbad s)
{
	cout << "string passed by reference: " << s << endl;
}

重要代码解释

static声明

static const int CINLIM = 80;

类成员变量当使用static静态变量声明时,该变量在全局数据区分配内存,不管开发人员新建多少个类对象,所有类对象共享一个CINLIM对象。

友元函数声明

friend bool operator<(const stringbad& s1, const stringbad& s2);
friend bool operator>(const stringbad& s1, const stringbad& s2);
friend bool operator==(const stringbad& s1, const stringbad& s2);
friend ostream& operator<<(ostream& os, const stringbad& s);
friend istream& operator>>(istream& is, stringbad& s);

如果运算符重载时,类的对象位与运算符右侧,将违背类调用的原则,即  类.operaor 运算符(); 因此想要灵活使用运算符重载(如果你不像灵活运用,也可以不用声明友元),需要声明其为友元函数,即非类对象也可以访问类的私有对象。

复制构造函数与赋值运算符重载

stringbad& operator=(const stringbad& s);
stringbad(const stringbad& s);

在类定义时定义了赋值运算符重载和类的复制构造函数重载,这是因为当没有复制构造函数时,如果直接 类1=类2,C++编译器将自行生成一个复制构造函数/赋值运算符,此时会忽略static变量num_strings,导致后续此变量调用时出现错误!解决的办法是自己定义一个显示的赋值构造函数。相信大家在其他博主的文章也看到了此项错误。

 

类的编写技巧

s = temp;//赋值运算符重载

此项本质为 类对象=字符串,此时会调用前列定义的赋值运算符重载,这种思想非常重要,我们可以在编写后续类成员函数时调用之前定义的一些构造函数或者运算符重载,这会有助于你开发代码~

 

类的引用传递与按值传递

void callme1(stringbad& s)//引用传递-原对象还在
{
	cout << "string passed by reference: " << s << endl;
}

void callme2(stringbad s)//按值传递
{
	cout << "string passed by reference: " << s << endl;
}

按值传递的后果是copy一个stringbad后新copy的类对象构造时无法匹配自己编写的构造函数,编译器自己会进行复制构造,如果你没有编写一个显示的复制构造函数,那这个时候num_strings没有++,这会导致后续的析构和变量调用出现问题。因此最好采用引用传递。

断点错误

作者自己在学习时,当编写完代码编译时,在程序运行的最后报出了断点错误~

网上看了一些文章,大家都指出是内存泄露或者是内存空间访问越界导致,于是逐句调试查了好半天new和delete,终于发现是下面这句错误:没有len+1

哭笑不得~属实傻逼了~大家引以为戒~

标签:const,动态内存,stringbad,C++,char,len,str,operator,断点
From: https://www.cnblogs.com/YiMo9929/p/16871494.html

相关文章

  • OpenCV C++双目三维重建:双目摄像头实现双目测距
    OpenCVC++双目三维重建:双目摄像头实现双目测距目录​​OpenCVC++双目三维重建:双目摄像头实现双目测距​​​​1.目录结构​​​​2.依赖库​​​​3.双目相机标定​​​​......
  • 【数据结构】例题:表达式求值 C++实现
    先写一个链栈#pragmaonce///链栈的结点类型template<classDataType>classStackNode{public: DataTypedata; StackNode*next; StackNode(){ next=nul......
  • C++面经 ----- C++11新特性:左值右值
    概念左值:可以取地址并且有名字的东西就是左值。右值:不能取地址的没有名字的东西就是右值。纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回......
  • C++ 面经 ----- C++11新特性:auto & decltype 类型推导
    C++11引入了auto和decltype关键字使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码。 auto示例autoa=10;//10是int型,可以自动推导......
  • 使用一条for语句求若干个整数的平均值--C++自学
    #include<iostream>#include<stdlib.h>usingnamespacestd;intmain(){intx,count=0,sum=0;cout<<"输入若干整数:"<<endl;cin>>x;for(;x!=......
  • 问题 I: 零基础学C/C++172——猴子选大王
    提示中也说了,这题可以用循环列表来实现,但是其实我也不怎么会哈哈哈哈,这题也同样可以用简单的基础语法来实现,只不过我们需要对一个循环语句做些手脚,让他头尾相连。点击查......
  • 问题 F: 零基础学C/C++176——生日相同问题
    首先题目也很明确的要求了按照日期从前到后,若日期相同,则比的是名字从短到长顺序输出,长度相同的按字典序输出。如果没有生日相同的学生,输出None。所以这题的一大难点也就......
  • 问题 N: 零基础学C/C++159——最长字符串
    题目一点也不难哦,就是要学会二维数组的输入输出但是不知为何这题有一个很奇怪的坑,如果你是AC:83%那么恭喜你掉坑里了!!这道题目竟然有一个检测点在最后的时候加\n确实......
  • 问题 M: 零基础学C/C++158——删除中间的*
    思路很简单,但实现起来有点麻烦。将前面2题融合(前两题我就觉得没必要放了哈哈哈哈),保留前面与后面的*都改成删除即可。你会发现我的代码是前两个的融合。要学会融会贯通鸭:......
  • C++ 何时需要使用 引用 & ?
    原因:在C++中,由于以下原因,变量通过引用传递:1)要修改调用者函数的局部变量:引用(或指针)允许被调用函数修改调用者函数的局部变量。例如,考虑以下示例程序,其......