C++ oop
先附上实例:
1 #pragma once 2 //String.h -- fixed and augmented string class define 3 #ifndef String_h_ 4 #define String_h_ 5 #include <iostream> 6 using std::cout; 7 using std::istream; 8 using std::ostream; 9 10 class String 11 { 12 private://默认private 13 char *str; 14 int len; 15 static int num_str; 16 static const int Ariz = 80; 17 public: 18 //constructor 19 String(const char* s="hello world"); //构造函数 20 //String(); //默认构造函数 21 String(const String &); //复制构造函数(初始化) 22 ~String(); 23 static int Howmany() { return num_str; }; 24 25 String& operator=(const String&); //类赋值 26 String& operator=(const char*); //字符串或地址赋值 27 String operator+(const String&)const; 28 char& operator[](int i); //对原(&)字符操作 29 const char& operator[](int i)const; //函数后接const 表示类方法不修改调用对象 30 operator char*() const; 31 32 friend bool operator<(const String& st1, const String &st2); 33 friend bool operator>(const String &st1, const String & st2); 34 friend bool operator==(const String &st1, const String& st2); 35 friend ostream& operator<<(ostream &os, const String &st);//&os别名、friend ostream& 别名的别名 36 friend istream & operator>>(istream &is, String &st); 37 int length()const { return len; } 38 char* c_str()const { return str; } 39 }; 40 #endif
1 #include<cstring> 2 #include "oopbase-String.h"//include<iostream> 3 using std::cout; 4 using std::cin; 5 //initializng static class number 6 int String::num_str = 0; 7 8 String::String(const char *s) 9 { 10 len = std::strlen(s);//or strler(s) 11 str = new char[len + 1]; 12 strcpy_s(str, len + 1, s); 13 num_str++; 14 cout << "const char *s:" << str << std::endl; 15 } 16 /* 17 String::String() 18 { 19 len = 0; 20 str = new char[1]; 21 str[0] = '\0'; 22 num_str++; 23 } */ 24 String::String(const String& st) 25 { 26 num_str++; 27 len = st.len; 28 str = new char[len + 1]; 29 strcpy_s(str, len + 1, st.str); 30 cout << "const String& st:" << str << std::endl; 31 } 32 String::~String(){ 33 num_str--; 34 delete[] str; 35 } 36 37 String& String::operator=(const String &st) 38 { 39 if (this == &st) 40 return *this; 41 delete[] str; 42 len = st.len; 43 str = new char[len + 1]; 44 //std::strcpy(str, st.str); 45 strcpy_s(str, len + 1, st.str); 46 return *this; 47 } 48 String& String::operator=(const char* s) 49 { 50 delete[] str; 51 len = strlen(s); 52 str = new char[len + 1]; 53 strcpy_s(str, len + 1, s); 54 //std::strcpy(str, s); 55 return *this; 56 } 57 String String::operator+(const String& st)const 58 { 59 String temp; 60 delete[] temp.str; 61 temp.len = len + st.len; 62 temp.str = new char[temp.len+1]; 63 strcpy_s(temp.str, temp.len+1, str); 64 strcat_s(temp.str, temp.len+1 , st.str); 65 return temp; 66 } 67 char& String::operator[](int i) 68 { 69 return str[i]; 70 } 71 const char &String::operator[](int i)const 72 { 73 return str[i]; 74 } 75 String::operator char*() const 76 { 77 return str; 78 } 79 80 bool operator<(const String& st1, const String &st2) 81 { 82 return (std::strcmp(st1.str, st2.str)); 83 } 84 bool operator>(const String& st1, const String &st2) 85 { 86 return st2 < st1; 87 } 88 bool operator==(const String&st1, const String &st2) 89 { 90 return std::strcmp(st1.str, st2.str); 91 } 92 ostream& operator<<(ostream& os, const String& s) 93 { 94 os << s.str; 95 return os; 96 } 97 istream& operator>>(istream& is, String &st) 98 { 99 char temp[80]; 100 is.get(temp, 80); 101 if (is) 102 st = temp; 103 while (is &&is.get() != '\n') 104 continue; 105 return is; 106 }
1 //usestring-- using expanded Sreing class 2 //compiled with String.cpp 3 #include "oopbase-String.h" //#include<iostream> 4 5 const int Ar = 10; 6 const int Max = 81; 7 8 int main() 9 { 10 using std::cout; 11 using std::cin; 12 using std::endl; 13 /* 操作① 14 String str = "hello "; 15 char *p = "world!"; 16 cout << (str + p) << endl; */ 17 /* 操作② 18 String str; 19 String str1("String str1"); 20 String str2="String str2"; 21 String str3=String("String str3=String(\" \")"); 22 String *str4 = new String("String *str4=new String(\" \")"); 23 //str1[10] = '5'; 24 //str2[10] = '6'; 25 String str5(str1); 26 String str6 = str2; 27 String str7 = String(str3); 28 String *str8 = new String(*str4); */ 29 return 0; 30 }
此例大致仿照string类进行简单改写,项目结构如下:
一般将程序分成三部分:
- 头文件:类声明,以数据成员的方式描述数据部分,以成员函数(也称方法)的方式描述公有接口。
- 源代码文件:类实现,描述如何实现类成员函数。
- 主函数文件:类使用包含调用与结构相关的 函数的代码。
这是一种非常有用的组织策略,两个源代码文件和头文件一起编译链接。当类比较小时也可以把类实现在头文件中完成,或者直接在主函数文件中完成类声明和类实现。
一、头文件
使用:#incluce "coordin.h",而不是<coordin.h>,如此c++编译器将在当前工作目录中 而非存储头文件主机系统的头文件系统中寻找。
作用:相当于将头文件中的全部代码移入#include位置,这在避免文件臃肿的同时 也增加了变量、函数重复声明定义的风险。
避免重复包含头文件:
#ifndef STRING_H_
#define STRING_H_
//place include file contents here
#endif
二、类声明
class String{ private:…;protected:…; public:… }; //class declaration
(1)类名首字母常大写。关键字private私有区 常用来描述数据部分,public公有区 常用来描述函数接口。
private、protected、public描述了在类使用时 对象对其成员的控制访问:
类对象①都可以直接访问共有public部分 ②但只能通过公有成员函数(或友元函数)访问对象的私有private成员和保护protected成员。
类对象成员访问方式是(.)或(->)运算符,与结构体变量使用相似。(注意类与对象的区别)
(2)作用域
在类声明时 类中定义的数据和函数名称作用域在整个类,在类外不可知。
在类实现时,若在类声明域外实现类成员函数时,使用作用域解析运算符(::)来标识函数所属的类。
(3)由所有String对象共享的名称,作用域为类的,在对象被创建前就存在 。实现方式:在声明或定义前加上static关键字,eg:
①static int num_strings; static const int Ariz=80; static int HowMany(); ②enum {Ariz=80}; Ariz为类内符号常量,
用以替代类内出现的Ariz为80。 使用String::Ariz, String::HowMany()
二、实现类的特殊函数
构造函数:创建对象时,对象会自动调用构造函数进行对象初始化。函数名称与类名相同,而且没有返回值 即没有声明类型。
1.一般构造函数:用类的一个或多个数据成员类型去初始化String类对象,eg: String(const char*);
2.复制构造函数:用一个已存在的String类对象去初始化另一个String类对象。C++编译器提供的该函数被调用时,
将使得两对象的数据部分完全等值。也可以自己重写 String(const String&); 。
3.默认构造函数:类对象在声明时调用,当你什么构造函数都没有时 C++编译器自动才会提供String();否则必须自行编写:
①String() ②String(const char* s="hello world"),所有参数都必须提供默认值才算是默认构造函数。在类实现时则不必写出默认值,以免重复
String::String(const char *s) { len = std::strlen(s);//or strler(s) str = new char[len + 1]; strcpy_s(str, len + 1, s); num_str++; cout << "const char *s:" << str << std::endl; } String::String(const String& st) { num_str++; len = st.len; str = new char[len + 1]; strcpy_s(str, len + 1, st.str); cout << "const String& st:" << str << std::endl; } String str; String str1("String str1"); String str2="String str2"; String str3=String("String str3=String(\" \")"); String *str4 = new String("String *str4=new String(\" \")"); String str5(str1); String str6 = str2; String str7 = String(str3); String *str8 = new String(*str4);
执行结果:
4.析构函数 构造函数创建对象后,程序一直对其跟踪,直至对象过期时调用对象的析构函数。其中,
静态存储类对象在程序执行完该对象定义所在代码块时调用;动态存储类对象在程序结束时调用。它与构造函数一头一尾配对。
C++编译器提供的析构函数调用时 收回对象的所有数据区。也可以自己重写 ~String();
三、运算符重载函数
重载op必须是有效的C++运算符,本质是函数重载(函数名相同,而参数列表和const等特征标不同),重新定义运算符(已有)。
1.类成员函数的格式为 operatorop(typename name); eg : operator+(char* p);
调用方式: ① str+p 解析为②str.operator+(p); 注意[]运算符的使用是str[st]。
赋值运算符 将已有对象赋给另一个对象时调用(假定st已存在) eg:①String str=st; ②str=st;
C++编辑器提供的默认赋值运算符 将st的所有数据部分的值赋给str,与复制构造函数相同。可以自己重写函数。
1 String& operator=(const char*); //字符串或地址赋值 in class declaration 2 String operator+(const String&)const; 3 char& operator[](int i){return str[i];} //对原(&)字符操作 const也是特征标,可以重载非重复 in class declaration 4 const char& operator[](int i)const{return str[i];} //函数后接const 表示类方法不修改调用对象 in class declatation 5 String& String::operator=(const char* s) 6 { 7 delete[] str; 8 len = strlen(s); 9 str = new char[len + 1]; 10 strcpy_s(str, len + 1, s); 11 //std::strcpy(str, s); 12 return *this; 13 } 14 String String::operator+(const String& st)const 15 { 16 String temp; 17 delete[] temp.str; 18 temp.len = len + st.len; 19 temp.str = new char[temp.len+1]; 20 strcpy_s(temp.str, temp.len+1, str); 21 strcat_s(temp.str, temp.len+1 , st.str); 22 return temp; 23 }
在该例中,返回值不能是引用String&,因为temp变量在函数结束便被删除。值得注意的是,temp.str,temp.len和st.str,st.len的访问方式。
类型转换 ①强制显示转换如(String)"hello world";②隐式转换如str+"hello world"; 字符串指针类型将调用一般构造函数转换为String类型。
转换函数 与类型转换相对,由String类型转换为其他自行定义的类型。格式:operator type(); eg : opetrator char*(); 使用:char* p=st;
当定义了多个转换函数时需要使用强制转换char* p=(char*)st;
2.非类成员的友元函数
非成员函数的格式: operatorop(typename1 name1,typename2 name2); eg : operator+(char* p, String& str);
调用方式:①p+str 解析为②operator+(p,str) 这就可以颠倒 1.类成员函数方式的参数顺序,但是 2.非成员函数方式不能访问str的私有数据,所以引入
友元函数的格式: 在函数声明前加上关键字friend,eg : friend ostream& operator<<(ostream &os, const String &st);
//&os别名、friend ostream& 别名的别名。 把函数名operator<<(…)视作变量名,把return的值视作该变量名初始化的值
特点:在类声明中声明,具有和成员函数一样的访问权限,但不是成员函数(所以不被继承)。
总结:一个类中必须有①一般构造函数+默认构造函数,这两者可以合而为一;②实现具体功能的业务函数
③当数据成员有指针类型时,需要自行定义复制构造函数、重载赋值运算符=和析构函数:因为通过复制或赋值的对象
eg;String st2=st1(st1是已存在的String对象) 这使得st2.str=st1.str,它们共享一块动态内存new char[len+1];
对st1.str的数据操作将改变st2.str