C++面向对象编程时禁止实例的拷贝

有时候我们需要禁止某一个类的实例的拷贝。拒绝实例的复制。
有两个比较简单的方法,一种是将拷贝构造函数设置为private并重载赋值运算符=到private中,并且不对它们进行实现:

class noncopy
{
...
private:
    noncopy(const noncopy&);
    const noncopy& operator=(const noncopy&);
};

这种方式的坏处就是每次都需要在类中多添加两个函数,还有一个更加简单的方法,编写一个拒绝复制的类,然后让其他类继承该类:

class disable_copy
{
protected:           //禁止生成disable_copy的实例
    disable_copy(){}
private:
    disable_copy(const disable_copy&);
    const disable_copy& operator=(const disable_copy&);
};

然后每次编写的禁止拷贝的类的时候都从disable_copy private继承:

class test:private disable_copy
{
    ...
};

注意

由于实例禁止拷贝,所以在将该实例传给函数的时候只能传引用或者指针:

class sample:private non_copy{
...
};

void function(sample)       //×
void function(sample&)      //√
void function(sample*)      //√

C++ vector迭代器的简单实现

一直觉得C++中的迭代器很神秘,而且也挺好用的。
今天我在研究C++ STL中的copy函数的时候突然想试试,看能不能实现同一个简单的vector。

这就是我今天下午写代码:

/*
 * Simple Vector
 *                  --By YUCOAT(www.yucoat.com)
 *
 *    Date: 2013-5-17
 */

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

template<typename Type>
struct print
{
    void operator()(Type& i)
    {
        cout << i << endl;
    }
};

template<typename T>
class MyVector
{
public:
    typedef T* iterator;
    MyVector(const int& size):_size(size),_first(NULL)
    {
        alloc();
    }
    void alloc()
    {
        try{
            _first = new T[_size];
        }catch(...){
            throw;
        }
    }
    ~MyVector()
    {
        if(NULL == _first)
            return;
        delete[] _first;
        _first = NULL;
    }

    iterator begin()
    {
        return _first;
    }
    iterator end()
    {
        return _first + _size;
    }
private:
    MyVector();
    MyVector(const MyVector&);
    int _size;
    iterator _first;
};

int main()
{
    int myints[] = {1, 2, 3, 4, 5, 6, 7};
    MyVector<int>myvector(7);

    std::copy(myints, myints + 7, myvector.begin());
    for_each(myvector.begin(), myvector.end(), print<int>());
    return 0;
}

STL中的iterator当然比我写的iterator复杂得多,也正是因为我不是这方面的高手,所以我才没有在这篇文章中花太多文字讲解iterator,因为我担心我的理解是错的,因为误人子弟。
这篇文章简单地用代码描述了一下iterator的原理,希望对各位读者有帮助。

归并排序C语言版

归并排序的过程是这样的:
假设有n个无序的元素,(下标分别为0、1、2…n-1)将他们划分成多个集合, 每两个为一个集合,集合内部进行排序,再将集合与集合合并,合并成一个大集合,大集合内部再使用插入排序,大集合与大集合合并,构成一个更大的集合。。。。直到所有的集合合并成一个集合。

以下是源代码:

#include <stdio.h>

void merge(int* array, int first, int mid, int last)
{
    int pos, temp, i;
    for((pos = mid + 1); pos <= last; pos++)
    {
        temp = array[pos];
        for(i = (pos - 1); (i >= first) && (array[i] > temp); i--)
        {
            array[i+1] = array[i];
        }
        array[i+1] = temp;
    }
}

void mergesort(int* array, int first, int last)
{
    if (first < last)
    {
        int mid = (first + last)/2;
        mergesort(array, first, mid);
        mergesort(array, mid + 1, last);
        merge(array, first, mid, last);
    }
}

int main()
{
    int array[10] = {42, 56, 12, 24, 87, 9, 475, 65, 17, 91};
    mergesort(array, 0, 9);
    int i;
    for (i = 0; i < 10; i++)
    {
        printf("%d\n", array[i]);
    }
    return 0;
}

AVL树的插入与旋转

关于AVL树我不说太多了,这里我只说两点,单旋转和双旋转,暂时不考虑左右

单旋转

单旋转的过程入下图所示

        8               6
       / \             / \
      6   *   --->   5   8  
     / \                 / \
    5   *               *   *
                          -----By YUCOAT.COM

双旋转

        8           8           6
       / \         / \         / \
      5   *  -->  6   *  -->  5   8  
     / \         / \         / \
    *   6       5   *       *   *

双旋转局势进行两次旋转,也就是当第一次旋转之后仍然满足不了要求的时候再进行一轮旋转。
双旋转的方式是

rotate_left(avl_ptr->left)
rotate_right(avl_ptr)

说得通俗一点,就是先往左旋一次,再往右旋一次。

什么时候进行双旋转,什么时候进行单旋转

假设avl_ptr所指向的节点为8,又假设刚被插入的值为n,那么

n > avl_ptr->left的时候进行双旋转
n < avl_ptr->left的时候进行单旋转
最后我奉上我昨天写的代码。。写完之后才发现,我写的跟书上的一模一样。。。好吧,我承认我一开始是在背书!

#include <iostream>
using namespace std;

#define MAX(a, b)  (a) > (b)?(a):(b)

struct avl_t
{
    int m_value;
    int m_height;
    avl_t* m_left;
    avl_t* m_right;
    avl_t(int& value):
        m_value(value), m_height(0), m_left(NULL), m_right(NULL)
    {}
};

int height(avl_t* p)
{
    return (NULL == p) ? -1: p->m_height;
}

void rotateleft(avl_t* &p)
{
    avl_t* temp = p->m_right;
    p->m_right = temp->m_left;
    temp->m_left = p;
    p->m_height = max(height(p->m_left), height(p->m_right)) + 1;
    temp->m_height = max(height(temp->m_left), height(p->m_right)) + 1;
    p = temp;
}

void rotateright(avl_t* &p)
{
    avl_t* temp = p->m_left;
    p->m_left = temp->m_right;
    temp->m_right = p;
    p->m_height = max(height(p->m_left), height(p->m_right)) + 1;
    temp->m_height = max(height(temp->m_left), height(p->m_right)) + 1;
    p = temp;
}

void insert_to_avl(avl_t*& k, int& value)
{
    if (NULL == k) {
        k = new avl_t(value);
    }
    else if (k->m_value > value) {
        insert_to_avl(k->m_left, value);
        if ((height(k->m_left) - height(k->m_right)) == 2) {
            if (value > k->m_left->m_value) {
                rotateleft(k->m_left);
                rotateright(k);
            }
            else {
                rotateright(k);
            }
        }
    }
    else {
        insert_to_avl(k->m_right, value);
        if ((height(k->m_right) - height(k->m_left)) == 2) {
            if (value <  k->m_right->m_value) {
                rotateright(k->m_right);
                rotateleft(k);
            }
            else {
                rotateleft(k);
            }
        }
    }
    k->m_height = max(height(k->m_left), height(k->m_right)) + 1;
}

void travels(avl_t* node)
{
    if (NULL != node) {
        travels(node->m_left);
        cout << node->m_value << '\t' << node->m_height << endl;
        travels(node->m_right);
    }

}
int main()
{
    int array[] = {8, 5, 47, 10, 0, 96, 88, 7, 3, 67};
    int i;
    avl_t* avl_tree = NULL;
    for(i = 0; i < 10; i++)
    {
        insert_to_avl(avl_tree, array[i]);
    }
    travels(avl_tree);
    return 0;
}

Bump Web Server 2编写的第一个阶段已经结束了

在前一篇博文中我有提到,我在写一个Web服务器,名叫Bump Web Server 2(简称Bump2)以高并发为目标。目前已经完成了第一个阶段的编写。

第一个阶段我做了什么?

第一个阶段就是让该程序有一些基本的功能,并且完成测试。写完之后也有点累,我还没有进行比较严格的测试,就是勉勉强强能够用吧!

在后一个阶段里面我希望能够尽可能地找出BUG并将他们修复。再者就是打磨其中的细节,比如http头部信息里面会有更多的信息,还有就是优化其性能,我现在还没有深入了解epoll和多线程,所以程序的性能还是有很大的提升空间。

PS:
今天下午收到同学的消息,说他在腾讯那边面试的时候顺利地进入了最后一轮面试,现在正在等结果,进腾讯应该十拿九稳吧。昨天我也接受了一轮来自淘宝的电话面试,我感觉我的表现不怎么样,不知道有没有机会参加复试机会。我真的希望这个暑假能够去Tengine团队实习,祝我好运吧!

在写另外一个http web服务器程序

我之前其实也写过http web服务器,名字叫Bump(查看源代码)。那大概是在四个月前写完的(其实我也忘记是多久写的,根据github的显示,我是四个月前提交的代码)。整个程序写的还算简单,勉强能用,写完之后欣喜若狂,那感觉就像我一个独立写完了一个nginx似的。过了四个月,我的水平有点提升,所以我决定再写一个,一来提升一下自身能力,再者拿来找工作也不错。

新版本的web服务程序还是叫Bump,加上版本号之后就是Bump2,貌似我特别喜欢Bump这个词,不管写什么东西都喜欢命名为Bump。上次写了一个播放器,命名为Bump Player(查看源代码),后来写了第二个版本,命名为Bp2(Bump Player Version 2, 查看源代码)。

Bump2 Web用的是epoll + 线程池模型,以高并发、高吞吐量为目标。我买了《Linux多线程服务端编程》这本书,由于之前在C++上面下的功夫很少,所以前面一些关于C++的内容看起来有点吃力。这本书我也没看多少,不过还是涨了姿势,学到了不少东西。这个版本的可靠性肯定会提升一个档次。

目前还没有写完,希望在五一假来临之前完成,Bump2没有所有的代码都是重写的,我之所以没有引用之前版本的代码是因为之前那个版本太烂了,这一定程度上说明了我之前那个版本的代码可维护性差,也从侧面反映我进步了,希望某一天我写Bump3的时候,Bump2里面大部分代码还能用。

Ubuntu 12.04 LTS编译安装Linux内核并打上CK补丁

也许是闲着没事,我突然想编译安装一下Ubuntu 12.04的内核,并打上CK补丁。
CK补丁能够提升桌面性能,YUCOAT的电脑是2G的内存,AMD X4 640的CPU,编译打上CK补丁的内核桌面程序运行速度明显提升。

什么是CK补丁

关于CK补丁,请参考Wikipedia的这个页面

选择一个合适的版本

YUCOAT在发这篇文章的时候,Linux内核的主版本号已经接近4了,但是这里,我仍然不采用最新稳定版的3.8内核。因为我在编译内核的时候会给内核打上一些补丁,而这些补丁不一定适合最新的版本。

你的操作系统未必是Ubuntu 12.04,但是你在选择内核版本的适合建议你尽量选某篇教程上所选用的版本,因为很多情况下某些版本在某些特定的环境下在编译的过程中会出一些大大小小的问题。如果选择有人试过水的版本,风险会少很多。

这里,我选择的版本号为3.3.4

打上合适的补丁

要打四个补丁,前三个是Debian提供的,最后一个就是ck补丁啦,
首先去Kernel.org选择某个本版的Linux内核,然后从下面的网站下载对应版本的补丁

http://kernel.ubuntu.com/~kernel-ppa/mainline/v3.3.4-precise/
#Debian官方的补丁需要这三个
0001-base-packaging.patch
0002-debian-changelog.patch
0003-default-configs.patch

#ck 补丁
http://ck.kolivas.org/patches/3.0/3.3/3.3-ck1/
这个补丁patch-3.3-ck1.bz2

开始打补丁

#把文件一一解压了,可以得到这些文件和目录
linux-3.3.4/
0001-base-packaging.patch
0002-debian-changelog.patch
0003-default-configs.patch
patch-3.3-ck1

#用patch命令打补丁
yucoat@yucoat ~/linux-3.3.4 $ cd linux-3.3.4
yucoat@yucoat ~/linux-3.3.4 $ patch p1 < ../0001-base-packaging.patch
yucoat@yucoat ~/linux-3.3.4 $ patch p1 < ../0002-debian-changelog.patch
yucoat@yucoat ~/linux-3.3.4 $ patch p1 < ../0003-default-configs.patch
yucoat@yucoat ~/linux-3.3.4 $ patch p1 < ../patch-3.3-ck1

配置config文件

如果你不怎么懂内核配置的话,你最好选择用以前版本的旧的配置

yucoat@yucoat ~/linux-3.3.4 $ cp /boot/config-3.2.0-35-generic .config
#开始配置
yucoat@yucoat ~/linux-3.3.4 $ make menuconfig
#如果你什么都不懂的话,最后在进入menuconfig之后保存再退出

#在编译的过程中某个模块出错,导致编译出现语法错误。为了避免这个错误,在编译前执行
yucoat@yucoat ~/linux-3.3.4 $ sed -i s/CONFIG_RTS5139=m/CONFIG_RTS5139=n/ .config

开始编译

需要安装一些工具

sudo apt-get install kernel-package libncurses5-dev
#开始编译
sudo make-kpkg --initrd --append-to-version=-ramki kernel_image kernel_headers -jN
#-jN中的N为cpu的核心数

编译完了之后会有两个文件,用dpkg命令安装这两个文件

sudo dpkg -i linux-image-3.3.4-ck1-ramki_3.3.4-..._amd64.deb
sudo dpkg -i linux-headers-3.3.4-..._amd64.deb

我在安装的过程中出现了一些小错误,是与dkms有关的,但是后来运行正常,所以也没有管它了。

最后一步,更新grub

sudo update-grub

重启!
本人并不具有丰富的编译内核的经验,对内核了解也比较少,文章难免照顾不周,如果有什么问题,请在下面留言。

C++智能指针auto_ptr与shared_ptr

对于C++程序来说,内存泄漏(memory leak)是一个很大的问题。由于C++不像Java那样自带垃圾回收机制。这使得C++的内存很容易被泄漏。为了应对这个问题,C++引入了智能指针(Smart pointer)。

智能指针的原理

一种通用的技术实现就是引入一个计数器,引用计数智能指针是一种生命期受管的对象,其内部有一个引用计数器,当内部的计数器为0时,这些对象会自动销毁自身的智能指针类。

STL中的auto_ptr

STL中所包含的智能指针就是auto_ptr啦,所需要的头文件为

#include <memory>

简单的示例:

#include <iostream>
#include <memory>

int main()
{
    std::auto_ptr<int> p(new int(10));
    std::cout << *p << std::endl;
    return 0;
}

std::auto_ptr的缺陷

std::auto_ptr对象不可作为STL容器的元素,缺少对动态配置而来的数组的支持,auto_ptr在被复制的时候会发生所有权的转移

Boost中的shared_ptr

STL中的auto_ptr的缺陷催生了其他智能指针的诞生,Loki库提供了SmartPtr,ATL提供了CComPtr和CComQIptr。C++11中则干脆废弃了auto_ptr,取而代之的是shared_ptr和unique_ptr。而这里要介绍的是Boost库提供的智能指针。
Boost一共提供五种智能指针
这里我只介绍shared_ptr

废话不多说,这里也有一段简单的示例

#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>

using namespace std;

int main()
{
    vector<boost::shared_ptr<string> >pt;
    pt.push_back(boost::shared_ptr<string>(new string("hello")));
    cout << *(pt.at(0)) << endl;
    return 0;
}

要注意的是smart_ptr不同于T*,也就是说,在创建一个智能指针的时候要明确得写出smart_ptrpt,其次就是不能将T*赋值给smart_ptr,再一个就是不能用ptr = NULL的方式将指针放空。

参考:《编写高质量代码:改善C++程序的150个建议》

Nginx开发的一些资料

大二的时候想阅读Nginx的源代码,然后仿照Nginx写一个简单的http服务器,后来才知道,是我把Nginx想得太简单了。因为当我下载了Nginx的源代码之后,我完全不知道该从哪里开始看,更别提看懂了。

今天我特意整理一下我收集到的Nginx开发资料。

首先推荐的是淘宝Tengine团队编写的Nginx开发教程。
Nginx开发从入门到精通

另外这里有一篇文章
Development of modules for nginx

最后就是Agentzh大神的新浪博客

写给我的博客诞生两周年之际

前两天我去给我的博客续费了,这是我第二次给我的博客续费,一年一次。
我的博客已经开头两年了,在这两年的光阴里,我不止一次想放弃这个博客。翻看我的博客更新记录就不难发现,我的博客发文日期是断断续续,难得看到长时间连续更新。不是我没有学习笔记可写,而是我懒得写或者由于学得不够深而不知道如何去写,但更好的解释是我想放弃这个博客。

刚开通这个博客的时候认识了不少像我一样博主,带着一番激情和好奇心出了100多块开通了一个博客,现在来看,好多博客已经销声匿迹了,剩下的要么难得更新一次,要么干脆就常常复制别人的文章,能够坚持下来的真的很少。

在接下来的一年里,我希望我能过得更加的充实,博客的内容也更加的丰富。到了明年的时候我相信我还在坚持!

无觅相关文章插件,快速提升流量