3、初识程序

数据结构静态的描述了数据元素之间的关系。

高效的程序需要在数据结构的基础上设计和选择算法。

高效的程序:恰当的数据结构+合适的算法

算法是特定问题求解步骤的描述:在计算机中表现为指令的有限序列。算法是独立存在的一种解决问题的方法和思想。特性:

输入:算法具有0个或多个输入。

输出:算法至少有1个或多个输出。

有穷性:算法在有限的步骤之后会自动结束而不会无限循环。

确定性:算法中的每一步都有确定的含义,不会出现二义性。

可行性:算法的每一步都是可行的。

正确性:算法对于合法数据能够得到满足要求的结果。算法能够处理非法输入,并得到合理的结果。算法对于边界数据和压力数据都能得到满足要求的结果。

可读性:算法要方便阅读,理解和交流。

健壮性:算法不应该产生莫名其妙的结果。

性价比:利用最少的资源得到满足要求的结果。

算法复杂度:

时间复杂度:算法运行后对时间需求量的定性描述。

空间复杂度:算法运行后对空间需求量的定性描述。

大O(operation)表示法:关注操作数量的最高次项。

6、算法效率

O(1)<O(logn)<O(n)<O(n*logn)<O(n*n)//<O(n*3)<O(2*n)<O(n!)<O(n*n)

算法的空间复杂度:S(n)=S(f(n)),n为算法的问题规模,f(n)为空间使用函数,与n有关 。

问题:在一个自然是1-1000中找出出现次数最多的数字

/*
    问题: 
    在一个由自然数1-1000中某些数字所组成的数组中,每个数字可能出现零次或者多次。
    设计一个算法,找出出现次数最多的数字。
*/
#include <iostream>
using namespace std;
void search(int a[], int len)     // O(n)
{
    int sp[1000] = {0};
    int max = 0;    
    for(int i=0; i<len; i++)
    {
        sp[a[i] - 1]++;   //作为sp数组的下标
    }    
    for(int i=0; i<1000; i++)
    {
        if( max < sp[i] )
        {
            max = sp[i];
        }
    }    
    for(int i=0; i<1000; i++)
    {
        if( max == sp[i] )
        {
            cout << i + 1 << endl;
        }
    }
}
int main(int argc, char* argv[])
{
    int a[] = {1, 1, 3, 4, 5, 6, 6, 6, 3, 3};    
    search(a, sizeof(a)/sizeof(*a));
    return 0;
}

 题目:当两个算法的大O表示法相同时,是否意味着两个算法的效率完全相同?

7、数据结构该怎么学习?

1,先从概念上形象的理解数据元素之间的关系。

2、思考这种关系能够解决什么问题。

3、考虑基于这种关系能够产生哪些算法。

4、理解和熟悉最终的算法。

5、选择一种熟悉的语言,编码实战。

8、泛型编程:

c++中的类模板:以相同的方式处理不同的类型。在类声明之前用template进行标识,<typename T>用于说明类中使用的泛型类型T。类模板的应用:只能显示指定具体类型,无法自动推导。使用具体类型<Type>定义对象。

#include <iostream>

using namespace std;

template <typename T>
void Swap(T& a, T& b)
{
    T t = a;
    a = b;
    b = t;

}

template <typename T>
class Op
{
public:
    T process(T v)
    {
        return v * v;
    }

};

int main()
{
    int a = 2;
    int b = 1;
    Swap(a, b);
    cout << "a = " << a << " " << "b = " << b << endl;
    double c = 0.01;
    double d = 0.02;
    Swap<double>(d, c);
    cout << "c = " << c << " " << "d = " << d << endl;
    Op<int> opInt;
    Op<double> opDouble;
    cout << "5 * 5 = " << opInt.process(5) << endl;
    cout << "0.3 * 0.3 = " << opDouble.process(0.3) << endl;
    return 0;

}

函数模板支持参数的自动推导和显示指定,类模板在使用时只能显示指定类型,类模板非常适用于编写数据结构相关的代码。

9、智能指针

指针生命周期结束时主动释放堆空间,一片堆空间最多只能由一个指针标识(拷贝构造函数,重载赋值操作符),杜绝指针运算和指针比较。

设计方案:通过类模板描述指针的行为,能够定义不同类型的指针对象。

重载指针特征操作符(->和*),利用对象模拟原生指针的行为。

创建DTLib  . 智能指针

#ifndef SMARTPOINTER_H_ 
#define SMARTPOINTER_H_
namespace WSlib
{
template <typename T>
class SmartPointer
{
T* m_pointer;
public:
SmartPointer(T* p=NULL)
{
m_pointer=p;
}
SmartPointer(const SmartPointer<T>& obj)
{
m_pointer=obj.m_pointer;
const_cast<SmartPointer<T>&>(obj).m_pointer=NULL; //const对象必须去除const属性
}
SmartPointer<T>& operator=(const SmartPointer<T>& obj)
{
if(this != &obj)
{
delete m_pointer;
m_pointer=obj.m_pointer;  //this->m_pointer=obj.m_pointer;
     //两者都是二元操作符,而且右边的操作数都是成员的名称。
             // 不同点:点运算符( . )的左边操作数是一个结果为结构的表达式;
             // 箭头运算符( -> )的左边的操作数是一个指向结构体的指针
const_cast<SmartPointer<T>&>(obj).m_pointer=NULL;  //const对象不允许赋值  必须去掉
}
return *this; //可以支持连续赋值
}
T* operator ->()
{
//return m_pointer;
}
T* operator* ()
{
return *m_pointer;
}
bool isNull()
{
return (m_pointer==NULL);
}
T* get()
{
return *m_pointer;
}
~SmartPointer()
{
delete m_pointer;
}
};
}

#endif

#include<iostream>
#include "SmartPointer.h"
using namespace std;
using namespace WSlib;  //必须使用自己命名空间
class Test
{
public:
Test()
{
cout<<"Test()"<<endl;
}
~Test()
{
cout<<"~Test()"<<endl;
}
};
int main()
{
SmartPointer <Test> sp=new Test(); //sp指向堆空间的地址,在析构函数中释放
    SmartPointer <Test>nsp;
nsp=sp;
//nsp++;  错的
cout<<sp.isNull()<<endl;
cout<<nsp.isNull()<<endl;
return 0;

}

智能指针的使用军规:智能用来指向堆空间的单个对象或者单个变量(不能是数组或者局部对象或者局部变量)

指针特征操作符(->和*可以被重载),重载指针特征符能够使用对象代替指针,智能指针智能用于指向堆空间中的内存,智能指针最大程度的避免内存问题。

11、异常的类型可以是自定义类类型,对于类类型异常的匹配依旧是至上而下严格匹配,赋值兼容性原则在异常匹配中依然适用。匹配子类异常的catch放在上部,匹配父类异常的catch放在下部。

异常功能定义:

Arithmenticption 计算异常

NullpointerException 空指针异常

indexOutOfBoundsException 越界异常

NoEnoughMemoryException内存不足异常

InvalidParameterException 参数错误异常

#ifndef EXCEPTION_H_
#define EXCEPTION_H_
namespace WSlib
{
   #define THROW_EXCEPTION(e, m) (throw e(m,__FILE__,__LINE__))
class Exception
{
protected:
char* m_message;  //指向字符串,异常详细信息
char* m_location; //异常地点
void init(const char* message,const char* file,int line);//重载了构造函数,构造函数内部逻辑差不多,利用辅助函数完成初始化


public:
Exception(const char* message);
Exception(const char* file,int line);
Exception(const char* message,const char* file,int line);


Exception(const Exception& e);
Exception& operator=(const Exception& e);
    
virtual const char* message()const;
virtual const char* location()const;


virtual ~Exception()=0;  //纯虚析构函数,用来说明当前类时抽象类
};
class ArithmeticException:public Exception
{
public:
ArithmeticException():Exception(0){}
ArithmeticException(const char* message):Exception(message){}
ArithmeticException(const char* file,int line):Exception(file,line){}
ArithmeticException(const char* message,const char* file,int line):Exception(message,file,line){}


ArithmeticException(const ArithmeticException& e):Exception(e){}
ArithmeticException& operator=(const ArithmeticException& e)
{
Exception::operator=(e); //直接调用父类的赋值操作符重载函数
return *this;
}


};
}

#endif

#include "Exception.h"
#include<cstring>
#include<cstdlib>  //malloc
using namespace std;
namespace WSlib
{
void Exception::init(const char* message,const char* file,int line)
{
//m_message=message;直接赋值不行,参数message指针指向字符串可能在栈,堆空间,全局数据区,没有办法控制message所指向的外部字符串的生命周期
m_message=strdup(message); //strdup将字符串复制到堆空间,m_message就指向堆空间的字符串,他的内容与message指向的字符串一样
if(file!=NULL)
{
char sl[16]={0};
itoa(line,sl,10);
m_location=static_cast<char*>(malloc(strlen(file)+strlen(sl)+2)); //malloc返回void* ,所以转换到插入*,+2一个是:一个是字符串结束符\0
m_location=strcpy(m_location,file);
m_location=strcat(m_location,":");
m_location=strcat(m_location,sl);
}
else
{
m_location=NULL;
}


}
Exception::Exception(const char* message)
{
    init(message,NULL,0);
}
Exception::Exception(const char* file,int line)
{
    init(NULL,file,line);
}
Exception::Exception(const char* message,const char* file,int line)
{
init( message,file, line);
}


Exception::Exception(const Exception& e)  //深拷贝,要保证每个对象内部的成员指针所指向的都是独立的堆空间
{
m_message=strdup(e.m_message);
m_location=strdup(e.m_location);

    Exception& Exception::operator=(const Exception& e)
{
if(this!=&e)
{
free(m_message);
free(m_location);
    m_message=strdup(e.m_message);
    m_location=strdup(e.m_location);
}
return *this;


}
const char* Exception::message() const
{
return m_message;
}
    const char* Exception::location() const
{
return m_location;
}
Exception::~Exception() //纯虚函数不提供实现,实现由子类完成,析构函数例外:c++规定,但凡自定义了析构函数,不管是不是纯虚函数,都要提供实现,因为析构对象时,最后都要调用父类析构函数,如果父类析构函数是纯虚函数并且没有实现,当调用到顶层析构函数时,没有实现不可以
{
free(m_message);
free(m_location);
}

}

#include <iostream>
#include "Exception.h"
using namespace std;
using namespace WSlib;
int main()
{
try
{
THROW_EXCEPTION(ArithmeticException, "test");
//throw Exception("test",__FILE__,__LINE__);  //文件名和行号使用预定义的宏 __FILE__,__LINE__
}
catch(const Exception&e)
{
cout<<"catch(const Exception& e)"<<endl;  //能捕获到子类对象,子类对象可以出现在任何需要父类对象的地方,赋值兼容性原则,所以能捕获到ArithmeticException,一个子类对象就是一个父类对象
cout<<e.message()<<endl;
cout<<e.location()<<endl;
}
catch(const ArithmeticException& e)          //异常匹配自上往下,捕获类型有继承关系,子类放上面,父类放下面,这个应该放上面
{
cout<<"catch(const Exception& e)"<<endl;  
cout<<e.message()<<endl;
cout<<e.location()<<endl;
}
return 0;

  //设计原则:在可复用代码库设计时,尽量使用面向对象技术进行架构,尽量使用异常处理机制分离正常逻辑和异常逻辑

//现代c++库必然包含充要的异常类族,所有库中的数据结构类都依赖于异常机制,异常机制能够分离库中代码的正常逻辑和异常逻辑

12、顶层父类

当代软件架构实践中的经验:尽量使用单重继承的方式进行系统设计,尽量保持系统中只存在单一的继承树,尽量使用组合关系代替继承关系。

不幸的事实:c++语言的灵活性使得代码中可以存在多个继承树,c++编译器的差异使得同样的代码可能表现不同的行为。

比如new操作失败会发生什么?  

创建Wobject类的意义:遵循经典设计准则,所有数据结构都继承自Wobject,定义动态内存申请的行为,提高代码的移植性。

#ifndef Wobject_H_
#define Wobject_H_
namespace WSlib
{
class Wobject
  {
public:
void* operator new(unsigned int size)throw();
void operator delete(void* p);
void* operator new[](unsigned int size)throw();
void operator delete[](void* p);
virtual ~Wobject()=0;
  };
}
#endif
//定义我们自己的new/delete重载函数,new/delete数据结构库中类的对象时,有我们期望的行为,在各个编译器都是一样的

//析构函数为纯虚函数,为抽象类,保证所有子类有虚函数表指针,能够使用动态类型识别相关的技术

#include<iostream>
#include "Wobject.h"
#include <cstdlib>
using namespace std;


namespace WSlib
{
void* Wobject::operator new(unsigned int size)throw()  //throw()不会抛出异常,失败返回空值
{
cout<<"Wobject::operator new"<<size<<endl;
return malloc(size);
}


void Wobject::operator delete(void* p)
{
cout<<"Wobject::operator delete"<<p<<endl;
free(p);
}
void* Wobject::operator new[](unsigned int size)throw()  //throw()不会抛出异常,失败返回空值
{
return malloc(size);
}
void Wobject::operator delete[](void* p)
{
free(p);
}
 Wobject::~Wobject()
{
 
}
}
/* 测试用例
#include <iostream>
#include "Wobject.h"
using namespace std;
using namespace WSlib;
class Test:public Wobject
{
public:
int i;
int j;
};
class Child:public Test
{
public:
int k;
};
int main()
{
Wobject* obj1=new Test();
Wobject* obj2=new Child();
cout<<"obj1"<<obj1<<endl;
cout<<"obj2"<<obj2<<endl;
//......
delete obj1;
delete obj2;
return 0;
}
****************************结果*******************************************
/*Wobject::operator new12 两个整形,一个虚函数表指针
Wobject::operator new16   又定义了一个整形k
obj100FFE238
obj200FFA1D8
Wobject::operator delete00FFE238
Wobject::operator delete00FFA1D8 释放对象空间的是我们自己定义的
**************************************************************************/
//Wobject类是WSlib中数据结构类的顶层父类,Wobject类用于统一动态内存申请的行为(失败返回空值,而不是抛出标准库中的异常对象),

//在堆中创建Wobject子类的对象,失败时返回NULL值,Wobject类为纯虚父类,所有子类都能进行动态类型识别

13、类族结构的进化

所有类位于单一的继承树,Wobject定义了动态内存申请失败时返回空指针。

改进的关键点,Exception类继承自Wobject类,堆空间中创建异常对象失败时,返回NULL指针。

新增InvalidOperation异常类:成员函数调用时,如果状态不正确则抛出异常。

SmartPoint类继承自Wobject类,堆空间中创建智能指针对象失败时,返回NULL 指针。

/* WSlib的开发方式和注意事项
迭代开发(可能推翻上一次的程序):每次完成一个小的目标,持续开发,最终打造可复用类库。原则:
单一继承树:所有类都继承自Wobject,规范堆对象创建时的行为
只抛异常,不处理异常:使用THROW_EXCEPTION抛出异常,提高移植性
弱耦合性:尽量不适用标准库中的类和函数,提高可移植性*/

猜你喜欢

转载自blog.csdn.net/ws857707645/article/details/80309841