存档

2012年2月 的存档

【笔记迁移】C++Primer笔记 2010/9/8

2012年2月29日  1,975 views 没有评论

13章复制控制


智能指针类的实现是通过使用计数类来管理指针成员的。计数类将指针成员再次封装,并含有一个计数器来记录有多少个对象包含这个指针成员。需要注意的是要将使用计数类的类设为友元。同时在计数类的友元类中要转换对指针的操作为对计数类的对象的操作。在复制的时候,首先将左操作数的计数类对象减一(如果计数器减一后为零,则删除对象),然后将右操作数的计数类对象赋值给做操作数,这样左右操作数共用同一个计数器类的对象,同一个指针只有一个计数器类的对象。析构函数中也要对计数类的计数器进行操作(减一,为零则删除)。

让智能指针负责删除共享对象,可以避免悬垂指针的出现。

class U_Ptr{
    friend class HasPtr;
    int *ip;
    size_t use;
    U_Ptr(int *p):ip(p),use(1){}
    ~U_Ptr(){delete ip;}
};//这是一个计数类的示例。

智能指针


值型类给指针成员提供值语义将对指针的操作变为对指针所指向的值的操作,复制值型对象时,会得到一个不同的新副本,对副本所做的改变不会反映在原有对象上,反之亦然。
这样就解决了所有因共享对象而产生的一系列的问题,但是同时也不可以使用共享对象了。


复制构造函数,赋值操作符,析构函数 这三者是紧密联系的,如果定义了其中一个,一般也要定义剩余的两个。这三个函数时“复制控制”函数的主要部分,
他们定义了复制、赋值或撤销该类型对象的含义,对特殊的成员进行管理。
定义复制控制函数最为困难的部分通常在于认识到他们的必要性。分配内存或其他资源的类几乎总是需要定义复制控制函数来管理所分配的资源。


14章重载操作符与转换


内置逻辑与(&&)和内置逻辑或(||)操作符使用短路求值,如果重新定义该操作符,丢失操作符的短路求值特征。


如果要将类用作关联容器键类型,则应定义<操作符和==操作符,因为许多算法假定这些操作符石存在的。例如sort算法使用<操作符,而find算法使用==操作符。
如果定义了==操作符,相应的也应该定义不等操作符!=。而如果定义了<或某个关系操作符,则应将全部四个关系操作符都定义(>,>=,<,<=)。

//重载了某个操作符,则应将其相关的其他操作符也要重载


成员与非成员操作符重载:

  • 成员操作符重载有一个默认的参数即当前对象的this指针,这是默认的左操作数,所以复制运算符=重载时只需要一个右操作数作为参数既可。ClassType& operator = (const ClassType&);
  • 当左操作数不是当前类的对象时,我们就必须使用非成员实现方式,例如输出操作符<<的左操作数应该是ostream对象。ostream& operator<< (ostream& out,const ClassType& s);//二元操作符重载时第一个参数为左操作数,第二个参数为右操作数。

一般非成员实现时要在类中显式声明此重载函数

成员与非成员


成员或非成员实现 :

  • 赋值(=)、下标([])、调用(())和成员访问箭头(->)等操作符必须定义为成员,将这些定义为非成员函数将在编译时出错。
  • 改变对象状态或与给定类型紧密联系的其他一些操作符,如自增、自减和解引用,通常应定义为成员函数。
  • 对称的操作符,如算术操作符、相等操作符、关系操作符和位操作符,最好定义为普通非成员函数。

成员与非成员


当重载输出操作符的时候所做的格式或应尽量的少尤其不要输出换行符,如果需要对输出进行格式化,我们应该让用户自己来控制输出细节。

阅读全文…

【笔记迁移】C++Primer笔记 2010/9/6

2012年2月29日  3,061 views 3 条评论

13章复制控制


编写自己的复制构造函数时,必须显式复制需要复制的任意成员。显示定义的复制构造函数不会进行任何自动复制。


即使对象赋值给自己,赋值操作符的正确工作也非常重要。保证这个行为的通用方法是显式检查对自身的赋值。最需要注意的是在赋值的时候做操作数的删除操作,如果不进行检查,
可能会出现先删除之后又将已经删除掉的数据赋值给做操作数,造成数据丢失。


【笔记迁移】C++Primer笔记 2010/9/12

2012年2月27日  1,657 views 没有评论

C++Primer笔记 2010/9/12


15章面向对象编程


继承和构造函数:在派生类的构造函数中不能通过初始化列表直接初始化继承成员的值,但是可以在初始化列表中包含基类的构造函数来初始化基类对象。派生类构造函数总是默认的先执行基类的构造函数然后再执行派生类的初始化列表,然后是派生类构造函数函数体。 在多重继承中,派生类只能初始化直接基类,因为直接基类对它的基类已经有了处理,所以派生类只需要对自己的直接基类进行初始化就可以了。
继承, 构造函数


继承和复制控制:派生类和基类的复制控制是分开的,当使用默认的复制控制函数的时候,首先调用基类的默认复制构造函数,然后调用派生类的复制构造函数。但是一旦派生类定义了自己的复制构造函数,那么该定义将完全覆盖默认定义,在此复制构造函数中必须显式调用基类的复制构造函数,否则将会调用基类的默认构造函数对基类对象进行初始化,这会出现令人难以理解的配置:它的基类部分将保存默认值,它的自有成员部分将是另一对象的副本。

示例:

class base{};
class child : public base{
    child(const child&amp; d):base(d)/* other member initialization */{}
}

继承, 复制构制造函数


继承和赋值操作符:赋值操作符必须防止自身赋值,这是首先要注意的。如果派生类显式定义了自己的赋值操作符,那么必须显式调用基类的赋值操作符来为基类对象赋值。
继承, 赋值操作符


继承和析构函数:派生类不需要注意基类的析构函数,编译器总是显示调用派生类对象的基类部分的析构函数,调用顺序与构造顺序相反。
继承, 析构函数


虚析构函数:为了能够在删除动态绑定指针时调用正确的析构函数,可以将基类的析构函数设为虚函数,这样删除时将调用动态绑定的真实对象的析构函数。基类析构函数几乎总是需要定
义为虚析构函数。而且虚析构函数和其他析构函数一样,如果层次中根类的析构函数为虚函数那么派生类的析构函数也将是虚函数,无论是否有显式定义。

析构函数, 虚函数


局部作用域中声明的函数不会重载全局作用域中定义的函数,派生类中定义的函数也不重载基类中定义的成员。所以说重载只会发生在同级作用域中,是一种横向操作。
如果不在派生类中定义此函数,那么可以使用派生类对象调用所有基类中此函数的重载版本,但是如果在派生类中定义了此函数名的函数,不管函数原型是否相同,都无法再通过派生类对象访问这些重载版本了。如果派生类希望使用自己定义的一个版本的此函数,同时又使用基类所定义的所有重载版本(这些版本的函数,函数原型各不相同)那么要么在派生类中重新定义所有的重载版本,要么为重载成员提供using声明。一个using声明只能指定一个名字,不带形参表。例如:在派生类中添加using base::out;就可以在派生类中重载基类的out成员了。

重载, 作用域, using


【笔记迁移】C++Primer笔记 2010/9/11

2012年2月26日  1,975 views 没有评论

15章 面向对象编程


引用和指针的静态类型(在编译时可知的引用类型或指针类型)与动态类型(指针或引用所绑定的对象的类型,这是仅在运行时可知的)可以不同,这是C++用以支持多态性的基石。


使用作用域限定符可以覆盖虚函数机制并强制函数调用使用虚函数的特定版本。
示例:

Item_base *baseP = &child;
double d = baseP ->Item_base::net_price(42);

这段代码将强制调用Item_base中定义的net_price版本,不会随baseP指针实际指向的对象而变化。
派生类虚函数调用基类版本时,必须显式使用作用于限定符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将会是一个自身调用,从而导致无穷递归。
虚函数


虚函数的默认实参的值由调用该函数的类型定义,与对象的动态类型无关。 在同一虚函数的基类版本和派生类版本中使用不同的默认实参几乎一定会引起麻烦。如果通过基类的引用或指针调用虚函数,但实际执行的是派生类中定义的版本(引用或指针所指对象实际为派生类对象),这时就可能会出现问题,此时,为虚函数的基类版本定义的默认实参将传递给派生类定义的版本,
而派生类版本是用不同的默认实参定义的。

虚函数, 默认实参


所有的基类与派生类的关系应该遵循 派生类应反映与基类的“是一种(is a)”关系。
继承


派生类可以恢复继承成员的访问级别。例如类A private实现继承类B,类B中有Public成员size,那么,在类A的public部分中增加using声明即可。
示例:

class A :private B{
public:
using B::size;
}

派生类可以恢复继承成员的访问级别,但是不能使访问级别比基类中原来指定的更严格或更宽松。
使用class保留字派生时默认具有private继承,使用struct时默认具有public继承,但是使用默认继承是非常罕见的,不论何时都应显式指明继承的派生保护级别。
继承, 访问级别


继承与静态成员:如果基类定义了static成员,则整个继承层次中只有这一个这样的成员。无论从基类派生出了多少个派生类,所有这些类在访问static成员时所访问的是同一个成员。
继承, static


基类与派生类的转换:派生类可以转换为基类但仅仅局限于引用与指针还有用派生类对象对基类对象进行初始化,初始化时派生类的自有部分被忽略。
从基类到派生类没有自动的转换,就算是基类的指针指向派生类对象,当用这个指针给派生类指针赋值时也会出现编译错误,因为编译器无法只掉动态的绑定是否安全,只能通过静态类型来盘判断操作是否安全,如果此操作真的安全,那么可以使用static_cast强制编译器进行转换,或者使用dynamic_cast申请在运行时检查。

继承, 转换


分类: 心得笔记 标签: , , ,

【笔记迁移】C++Primer笔记 2010/9/9

2012年2月26日  2,436 views 2 条评论

Google的NoteBook这回是真的要没了,以前上学时在NoteBook上做了一些笔记,现在都已经被直接导入到Google Docs里了,因为Google Docs不是那么好访问,现在就将之前的这些笔记都迁移到这里来,现在再看这些笔记,有些自己都有点看不懂了。若有时间的话应该再整理一下。

参加工作半年多了,自从去年9月份开始就没有再更新过这个博客了,若不是前几天域名商发邮件提醒我续费,没准已经将这个博客给遗忘了……唉,生活是艰苦的,工作是忙碌的,无限怀念学生时代啊。

14章重载操作符与转换


1、自增自减操作符重载

自增自减的前缀式与后缀式的区分靠的是参数表中的int参数,这个形参是无用的,它唯一的目的是使后缀函数与前缀函数区分开来。后缀式写这个参数,前缀式没有。
自增自减操作符的后缀式重载时,应与内置操作符一致,返回旧值(即尚未自增或自减的值),并且返回值而不是引用。
示例:

前缀式 ClassType& operator++();

后缀式 ClassType operator++(int); // 一般情况下,后缀式用前缀式实现。


2、调用操作符重载()

定义了调用操作符的类,其对象常称为函数对象(Function Object),即它们是行为类似函数的对象。

struct absInt{
int operator() (int val){
return val&lt;0?-val:val;
}
};
int i=42;
absInt absObj;
unsigned int ui = absObj(i);

3、标准库函数对象调用

标准库函数对象使用时要包含functional头文件,有算术函数对象类型、关系函数对象类型、逻辑函数对象类象,就是各种操作符的函数对象,例如plus<Type> 对应+。
用法:函数对象通常用于覆盖算法使用的默认操作符。如sort默认使用operator<按升序对容器进行排序。
如果要降序排列则可以传递函数对象greater。 示例:

sort(svec.begin(),svec.end(),greater<string>());

标准库函数对象


4、函数对象的函数适配器

函数对象的函数适配器有两类

  • 绑定器(binder),它通过讲一个操作数绑定到给定值而将二元函数对象转换为一元函数对象。有bind1st和bind2nd两个,bind1st将给定值绑定到二元函数对象的第一个实参,bind2nd绑定到第二个实参。
  • 求反器(negator),它将谓词函数对象的真值求反。有not1和not2两个,not1将一元函数对象的真值求反,not2将二元函数对象的真值求反。

示例:

//计算容器vec中所有小于或等于10的元素的个数。bind2nd后面等于 <=10,将二元函数对象变为一元。
count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 10) );

//计算容器vec中所有不是小于等于10的元素,即大于10的元素,not1将bind2nd(less_equal<int>(), 10)的返回值求反。
count_if(vec.begin(), vec.end(), not1(bind2nd(less_equal<int>(), 10) );

5、转换操作符

转换操作符(conversion operator)是一种特殊的类成员函数。它定义将类类型值转变为其他类型值的转换。转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型。

通用形式:operator type();//没有返回值类型但是有返回值
这里,type表示内置类型名、类类型名或有类型别名所定义的名字。对任何可作为函数返回类型的类型除void外都可以定义转换函数。
转换函数必须是成员函数,不能指定返回值类型,并且形参表必须为空。
类类型转换之后不能再跟另一个类类型转换。如果需要多个类类型转换,则代码将出错。
转换操作符


分类: 心得笔记 标签: , , ,