C++手稿:std::string

C++ 字符串

字符串在很多编程语言中已经成为基本数据类型,C语言中我们使用char*来手动维护字符串的内存, 在C++中,可以使用std::string来方便地创建和操作字符串。

string是一个模板类,它有basic_string<T>定义:

typedef basic_string<char> string;

C++的string可以通过成员方法c_str()转换为C语言的char*

参考文档:cplusplus.com/string

初始化与赋值

string有两个常用的构造函数:

// 用一个C字符串构造
string str("hello");
// 等价于
string str = "hello";

也可以用N个同样的字符来构造字符串:string str2(8, 'x')

在C0x标准中,std::to_string可以将很多类型转换为一个string,可以代替itoa,例如:

string str = to_string(123);

string构造函数不接受charint类型。

字符串可以直接互相赋值,内存会自动拷贝和销毁,我们大可不必管它。对于单个字符赋值可以使用下标运算符:

for(int i=0;i<str.length(); i++){
    str[i] = 'a';
}

与多数class类似,string也提供了swapstr1.swap(s2)将会交换二者的值。

运算符支持

有通用运算符支持的数据类型往往更容易理解和操作,其中最讨人喜欢的莫过于+运算符:

str += str2;
str = str + "hello";

当然,你也可以直接调用append方法:str.append(str2)

除了+string还支持一系列的比较运算符:<, ==, >, <=, >=, !=

当然,你仍然可以直接调用compare方法:str1.compare(str2)str1小则会返回-1

C++手稿:封装与继承

C++ 封装 继承 作用域 多继承 名称隐藏 对象组合 构造函数 析构函数

本文总结了C++中类的继承相关的概念,包括可见性级别、继承的不同方式、构造与析构过程、封闭类、友元等。

可见性级别

C++类提供了数据结构和算法的封装,以及相应的3种可见级别。它们定义了不同的可见性:

  • Public:当前类以及子类的方法中可见,外部可见。
  • Protected:当前类以及子类的方法中可见,外部不可见。
  • Private:当前类的方法中可见,外部不可见。

在一个对象的成员函数中,可以调用其他同类对象的私有方法。

多数现代的面向对象语言中,仅提供Private和Public两种可见性,C++的可见级别略显复杂。 然而三种继承方式以及多继承机制,让问题更加复杂。简单起见,此处只讨论Private和Public方式的单继承。

  • Public继承:子类中可访问基类publicprotected成员,子类外部可见父类public成员。
  • Private继承:子类中可访问基类publicprotected成员,子类外部不可见父类成员。

类的继承

  • Public继承表示”is a”的关系(见Effective C++: Item 32),子类的对象同时也是一个基类的对象。 子类的行为应符合基类的行为,因此Public继承中通常不会覆盖基类成员。

  • Private继承表示“以…实现“的关系,子类是以基类来实现的 对于一个子类的对象,其外部不可见基类的行为。Private继承更像是对象组合。

对于Public继承,子类的指针、引用、变量可以直接赋值给基类的指针、引用、变量。

class CBase{};
class CDerived: public CBase{
public:
    CDerived(): CBase(){}
};

C++手稿:虚函数与多态

C++ 多态 引用 指针 继承 虚函数 成员函数 构造函数 析构函数

C++类继承带来了诸多好处:基类代码复用、通用的方法和属性、更好的可维护性, 然而最大的好处莫过于提供统一的接口。接口是一种对类型的抽象,它统一了一系列类的行为, 不同类的对象之间交互更加容易。Java、objective C等面向对象语言都提供了接口的概念, 在C++中可以通过抽象类来模拟接口的行为。

与此同时,C++通过虚函数实现了多态:通过基类指针或引用调用虚函数时,会调用当前对象的实际类型中声明的函数。 为了这个特性,包含虚函数的C++对象中会存储一个虚函数表指针,来完成动态联编。

编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序运行时才能确定将要调用的函数, 为此要确切知道该调用的函数,要求联编工作要在程序运行时进行, 这种在程序运行时进行联编工作被称为动态联编

jQuery事件:bind、delegate、on的区别

AngularJS DOM HTML JavaScript jQuery 事件

最近在AngularJS的开发中,遇到一个神奇的事情:我们用到livebox来预览评论列表中的图片, 然而评论列表是由Angular Resource动态载入的。不可思议的是,点击这些动态载入的图片仍然会触发lightbox的图片预览。 难道lightbox使用先进的MutationObserver技术监听了DOM的变化?观察lightbox源码才发现,原来只是jQuery的.on()方法:

$('body').on('click', 'a[rel^=lightbox], ...', function(event){});

本文便来详解各种jQuery事件绑定方法:onbinddelegateliveunbindtrigger。 同时总结一下常用的jQuery事件技术:如何阻止事件冒泡、阻止浏览器默认行为、解绑事件处理函数、自定义事件。

什么是 jQuery 事件

jQuery事件是DOM事件的封装,同时支持自定义的扩展。在程序设计中,事件和代理有着相似的作用: 它们提供了一种机制,使得行为的实现方式和调用时机可以分离。

不谈jQuery,DOM本身就提供了一系列的javascript事件,例如clickkeyupsubmit。 未实现相关业务逻辑,通常会为这些事件定义一系列的处理函数,处理函数定义了业务的实现方式,而浏览器知道这些业务的调用时机。 Javascript事件就是这样一种机制,使得行为的实现方式和调用时机可以动态地绑定。

jQuery事件是通过封装javascript事件来实现的,例如.keyup()便是onkeyup的封装:

.keyup(): Bind an event handler to the “keyup” JavaScript event, or trigger that event on an element.

除了封装大多数的javascript事件,jQuery提供了统一的事件绑定和触发机制:

  • 绑定事件:bindonlivedelegatekeyup(<function>)
  • 触发事件:trigger('keyup')keyup()
  • 解绑事件:unbindoffdieundelegate

事件绑定:bind

使用javascript绑定一个事件很简单,只需要在HTML中设置onxxx属性, 并且在javascript中定义相关的处理函数便可以完成。

<div onclick="func()"></div>
<script>
function func(){
    console.log('clicked!');
}
</script>

上述是基本的javascript事件处理方式,而jQuery提供了更加方便的方式:.bind()函数。

.bind():Attach a handler to an event for the elements.

<div id='foo'></div>
<script>
$('#foo').click(function(){
    console.log('clicked!');
});
</script>

.click(<function>)等效于.bind('click', <function>)。另外还可以通过unbind来解绑事件:

$('#foo').unbind('click');

如果unbind参数为空,则解绑匹配元素的所有事件处理函数。 在我的理解中,我们还是不要offunbinddie吧。即使不谈效率,它们也使得软件更难理解了。 如果你感觉有需要,下面的.on()应该会满足你~

.bind将会给所有匹配的元素都绑定一次事件,当元素很多时性能会变差。 而且后来动态新增的元素不会被绑定。

C++手稿:运算符重载

C++ 引用 运算符 成员函数 构造函数 链式调用 赋值运算符 运算符重载

运算符重载就是对已有的C++运算符赋予更多的语义,让一个运算符可以作用于其他的数据类型。 典型地,让运算符接受一个类的对象作为参数。

通常有两种方式来重载一个运算符:

  1. 声明一个普通函数,作为类的友元。
  2. 声明为类的成员方法。

事实上,运算符的本质是函数。每个运算符调用会转换为函数调用,运算符的操作数转换为函数参数。 运算符的重载本质上是方法的重载。

这些运算符不允许重载:..*::?:sizeof

重载为普通函数

重载为普通函数时,参数的个数为运算符的目数:

CPerson operator+(const CPerson& male, const CPerson& female){
    return CPerson(male.name + female.name);
};

CPerson male("Bob"), female("Alice");
CPerson child1 = male + female;
// 等价于
CPerson child2 = operator+(male, female);

这些运算符必须重载为成员函数:()[]->=

重载为成员函数

重载为成员函数时,参数的个数为运算符的目数-1:

class CPerson{
    string name;
public:
    CPerson(string name_):name(name_){}
    CPerson operator+(const CPerson& female){
        return CPerson(name + female.name);
    }
};

CPerson male("Bob"), female("Alice");
CPerson child1 = male + female;
// 等价于
CPerson child2 = male.operator+(female);

C++手稿:类的静态和常量成员

C++ 常量 类型转换 静态变量 赋值运算符

本文总结了静态成员的使用、单例的实现、常量对象与常量方法,以及如何将常量方法重载为普通方法。

静态成员

对象成员的声明前加static即可定义为静态成员,静态成员必须在声明类的文件中进行声明(通常会初始化),否则链接错。 访问静态成员可以通过类名,也可以通过对象名。

class CPerson{
    static int count;
};
int CPerson::count = 0;

CPerson p1, &p2 = p1, *p3 = new CPerson();

cout<<CPerson::count<<endl;
cout<<p1.count<<endl;
cout<<p2.count<<endl;
cout<<p3->count<<endl;
  • 只有静态常量整型才可以在类的声明中,直接初始化。
  • sizeof运算符不会计算静态常量。
  • 静态方法不可访问非静态成员、this指针。

单例的实现

在C++中,借由静态成员变量,可以实现单例模式。首先需要将构造函数私有化,并提供获取单例的方法。 此后还需禁止复制构造函数、禁止赋值运算符。

class CPerson{
private:
    static CPerson* p;
    CPerson(){};
    CPerson(CPerson&);
    const CPerson& operator= (const CPerson&);
public:
    static Person* instance(){
        return p ? p : (p = new P());
    }
};
CPerson* CPerson::p = NULL;

上一页 下一页