目录
1.指向对象的指针
// class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
#include<iostream>
using std::istream;
using std::ostream;
class String
{
private:
char* str; //字符串指针,此处没有分配内存,后续用new来为其开辟内存空间
int len;
static int num_strings; //只有static,则必须声明后在实现文件中进行初始化
static const int CINLIM = 80; //const static 可以直接在类中初始化
public:
String(const char* s);
String();
String(const String& s);//赋值构造函数
~String(); //在使用new的类中十分重要 -- 配套使用delete来释放堆空间
int length() const
{
return len;
};
String& operator=(const String& st); //对象赋值
String& operator=(const char* s); //一般字符串赋值
char& operator[](int i); //重载 中括号[] 运算符 //可改变数据版本
const char& operator[](int i)const; //不改变数据版本
friend bool operator<(const String& st1, const String& st2);
friend bool operator>(const String& st1, const String& st2);
friend bool operator==(const String& st1, const String& st2);
// ostream流和istream流没有共用的复制构造函数,所以必须要返回引用且按引用传递参数,若返回的是一个值或者按值传递参数,则使用到了副本且必须使用复制构造函数。
friend istream& operator>>(istream& is, String& st); // cin>>
friend ostream& operator<<(ostream& os, const String& st); // cout<<
//静态函数
static int HowMany();
};
#endif
// fuc.cpp
#include<cstring> // strcpy--复制字符串, strcmp--根据ASCII码值比较字符串,strlen--获取字符串的长度
#include"class.h"
using namespace std;
//为类中声明的static变量初始化,且要声明其所在的类区域
int String::num_strings = 0;
int String::HowMany()
{
return num_strings;
}
String::String(const char* s)
{
len = std::strlen(s); //strlen获取的是字符串除去末尾空字符的长度,所以长度要为 len+1;
str = new char[len + 1]; //在构造函数中使用new申请堆空间,在析构函数中使用delete释放堆空间
strcpy(str, s);
num_strings++;
}
String::String()
{
len = 4;
str = new char[len + 1];
str[0] = '\0'; //设置为空指针,也可为设置为 NULL, nullptr,(void *)0q
num_strings++;
}
String::String(const String& s)//复制构造函数
{
num_strings++; //为新初始化的对象复制,所以可以在总数上加上1
len = s.len;
//深度复制:
str = new char[len + 1];
strcpy(str, s.str);
}
String::~String() //在使用new的类中十分重要 -- 配套使用delete来释放堆空间
{
--num_strings;
delete[] str;
}
String& String::operator=(const String& st) //对象赋值
{
if (this == &st) //如果调用对象和参数已经相同
{
return *this;
}
delete[] str; //先删除之前的内存,防止内存的浪费
len = st.len;
//深度复制
str = new char[len + 1];
strcpy(str, st.str);
return *this; //使得可以进行连续复制操作
}
String& String::operator=(const char* s) //一般字符串赋值
{
delete[] str;
len = strlen(s);
str = new char[len + 1];
strcpy(str, s);
return *this;
}
char& String::operator[](int i) //重载 中括号[] 运算符 //可改变数据版本
{
return str[i]; //即返回调用对象字符串的第i个字符
}
const char& String::operator[](int i)const//不改变数据版本
{
return str[i];
}
//友元函数:
bool operator<(const String& st1, const String& st2)
{
return (strcmp(st1.str, st2.str) < 0);
}
bool operator>(const String& st1, const String& st2)
{
return st2 < st1; //直接使用上面刚重载的运算符 逻辑:若语句为: st1>st2,若成立则应当有st2<st1,可以实现函数功能
//或 return (strcmp(st1.str,st2.str)>0)
}
bool operator==(const String& st1, const String& st2)
{
return (strcmp(st1.str, st2.str) == 0);
}
istream& operator>>(istream& is, String& st) // cin>>
{
char temp[String::CINLIM]; //友元函数,不能直接使用类中的静态常量,需要使用作用域解析运算符
is.get(temp, String::CINLIM); // 相当于cin,get(name,size)
if (is)
{
st = temp; //使用的重载过的对于常规字符串的赋值运算符
}
//while的作用:读取掉剩下的多余出来的字符
while (is && is.get() != '\n') // 若输入失败,则istream对象is的值将被置为false;
{
continue;
}
return is;
}
ostream& operator<<(ostream& os, const String& st) // cout<<
{
os << st.str;
return os;
}
// main.cpp
#include<iostream>
#include<cstdlib>
#include<ctime>
#include"class.h"
const int Arsize = 10;
const int MaxLen = 81;
int main()
{
using namespace std;
String name;
cout << "Hi, what's your name?" << endl;
cin >> name; // >>运算符的重载,使可以作用于对象
cout << name << ", please enter up to " << Arsize
<< " short sayings <empty line to quit>:" << endl; // <<运算符的重载,使可以作用于对象
String sayings[MaxLen];
char temp[MaxLen];
int i;
for (i = 0; i < Arsize; i++)
{
cout << i + 1 << ": ";
cin.get(temp, MaxLen); // cin.get(stringname,stringlength)
while (cin && cin.get() != '\n') //当没有输入错误且没有读取完多余的字符时,继续循环读取
{
continue;
}
if (!cin || temp[0] == '\0') //如果输入为空, break退出
{
break;
}
else
{
sayings[i] = temp;
}
int total = i;
if (total > 0)
{
cout << "Here are your sayings: " << endl;
for (i = 0; i < total; i++)
{
cout << sayings[i] << endl;
}
//使用指向对象的指针来保存特殊的字符串
String* shortest = &sayings[0];
String* first = &sayings[0];
for (i = 1; i < total; i++)
{
if (sayings[i].length() < shortest->length())
{
shortest = &sayings[i];
}
if (sayings[i] < *first)
{
first = &sayings[i];
}
}
cout << "Shortest sayings: " << *shortest << endl;
cout << "First alphabetically: " << *first << endl;
srand(time(0)); //为rand()提供随机数种子 cstdlib和ctime头文件中的函数
int choice = rand() % total;
String* favorite = new String(sayings[choice]);
cout << "My favorite sayings: " << *favorite << endl;
delete favorite;
}
else
{
cout << "Not much to say,eh?" << endl;
}
cout << "Bye~" << endl;
return 0;
}
}
知识点:
1.ostream流和istream流没有公共的复制构造函数,在重载此类的运算符时必须要返回引用且按引用传递参数。若返回的是一个值或者按值传递参数,则使用到了副本且必须使用复制构造函数。
eg: friend ostream& operator<<(ostream & os, const String & st)
2.创建指向对象的指针的语法: classname * name; (与创建指向一般变量的指针语法是相同的)
3.使用指向对象的指针: 访问对象中的公共函数: name->fucname (间接访问使用 -> 而不是 '.'),使用(*name)来进行解引用。
4.String* favorite = new String(sayings[choice]) 和 String* favorite = new String[number]的区别:前者是为favorite这个指向对象的指针初始化为sayings[choice],后者是创建一个指针数组,其中共有number个指针。
2.使用定位new运算符为对象分配内存空间
#include<iostream>
#include<string>
#include<new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
string words;
int number;
public:
JustTesting(const string& s = "Just Testing", int n = 0) //所有参数均为默认参数的自定义构造函数
{
words = s;
number = n;
cout << words << " constructed" << endl;
};
~JustTesting()
{
cout << words << " destroyed" << endl;
}
void Show() const
{
cout << words << " , " << number << endl;
}
};
int main()
{
char* buffer = new char[BUF]; //创建内存区
JustTesting* pc1, *pc2; //创建两个指向对象的指针
pc1 = new(buffer) JustTesting;
pc2 = new JustTesting("Heap1", 20); //普通new,并进行初始化
cout << "Memory block addresses:\n" << "buffer: "
<< (void*)buffer << " heap: " << pc2 << endl;
cout << "Memory contents: \n";
cout << pc1 << ": ";
pc1->Show();
cout << pc2 << ": ";
pc2->Show();
JustTesting* pc3, * pc4;
pc3 = new(buffer + sizeof(JustTesting)) JustTesting("Better idea", 6);
pc4 = new JustTesting("Heap2", 10);
cout << "Memory contents: " << endl;
cout << pc3 << ": ";
pc3->Show();
cout << pc4 << ": ";
pc4->Show();
delete pc2;
delete pc4;
//对于使用定位new分配内存的对象,必须要显式的调用析构函数来销毁对象,且删除顺序应当与创建的顺序相反 -- 先进后出,后进先出
//同时,应当先销毁对象,再释放为其分配的内存空间
pc3->~JustTesting();
pc1->~JustTesting();
delete[]buffer;
cout << "Done!" << endl;
return 0;
}
知识点:
1.如果自定义了构造函数则必须定义一个默认构造函数,但若是自定义的构造函数中的所有参数都是默认参数,则无需再定义一个默认构造函数。
eg: JustTesting(const string &s = "just testing",int n =0) -- 所有参数都有默认值
2.使用定位new需要包含头文件<new>
3.定位new的语法:typename * name = new (buffer) typename
eg:JustTesting * pc1 = new(buffer) JustTesting;
4. 若多个对象使用一个buffer缓冲区,则需要加上偏移量( sizeof() ),防止内存的覆盖
eg:JustTesting * pc3 = new(buffer + sizeof(JustTesting)) JustTesting("Better idea", 6);
5.对于使用定位new分配内存的对象,必须要显式的调用析构函数来销毁对象,且删除顺序应当与创建的顺序相反 -- 先进后出,后进先出。同时,应当先销毁对象,再释放为其分配的内存空间。
eg:pc3->~JustTesting();
pc1->~JustTesting();
delete[]buffer;