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
String.h
  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 }
String.cpp
 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 }
main.cpp

 此例大致仿照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

 

posted @ 2019-01-17 12:33  shines87  阅读(417)  评论(0编辑  收藏  举报