C++学习杂笔(总)

私有构造函数

通常我们都将构造函数的声明置于public区段,假如我们将其放入private区段中会发生什么样的后果?
我们知道,当我们在程序中声明一个对象时,编译器为调用构造函数(如果有的话),而这个调用将通常是外部的,也就是说它不属于class对象本身的调用,假如构造函数是私有的,由于在class外部不允许访问私有成员,所以这将导致编译出错。
对于class本身,我们还可以利用它的static公有成员,因为它们独立于class对象之外,我们不必产生对象也可以使用它们。
因为构造函数被class私有化了,所以我们要创建出对象,就必须能够访问到class的私有域;但这一点“我们”是做不到的,那么,谁能做得到呢?class的成员可以做得到;但在我们建构出其对象之前,怎么能利用它的成员呢?噢,刚才我们刚刚提到了static公有成员,它是独立于class对象而存在的,当然,它也是公有的,“我们”可以访问得到。假如在某个static函数中创建了该class的对象,并以引用或者指针的形式将其返回(不可以以值的形式返回,想想为什么),我们就获得了这个对象的使用权。下面是例子:

class WonderfulClass
{
public:
       static WonderfulClass* makeAnObject()
       {
              // 创建一个WonderfulClass对象并返回其指针
              return (new WonderfulClass);   //在堆上创建对象
       }
private:
       WonderfulClass() { }  //私有构造函数
};

int main()
{
       WonderfulClass *p = WonderfulClass::makeAnObject();  //此处只能通过作用域符访问静态成员

       ... // 使用*p

       delete p;  // Not neccesary here, but it's a good habit.

       return 0;
}

makeAnObject()作为WonderfulClass的静态成员函数,尽心尽责地为我们创建对象:由于要跨函数传递并且不能使用值传递方式,所以我们选择在堆上创建对象,这样即使makeAnObject()退出,对象也不会随之蒸发掉,当然,使用完之后你可不要忘了手工将它清除。

除了公有的static成员可以帮助我们访问私有域外,可以使用该类的友元函数或者友元类创建其对象,这里就不举例了。

嗯,例如,我们想实现这样一个class:它至多只能存在一个,或者指定数量个的对象(还记得标准输入输出流库中那个独一无二的cout吗?),我们可以在class的私有域中添加一个static类型的计数器,它的初值置为0,然后再对makeAnObject()做点手脚:每次调用它时先检查计数器的值是否已经达到对象个数的上限值,如果是则产生错误,否则才new出新的对象,同时将计数器的值增1.最后,为了避免值复制时产生新的对象副本,除了将构造函数置为私有外,复制构造函数也要特别声明并置为私有。

————————————————————————————————————————————————————

1、常量迭代器 (const_iterator)

vector vv(10,9);
const vector :: iterator iter = vv.begin();
则,当程序中出现这样的语句时是错误的
++iter;
其原因是iter是一个常量,因此是不能改变的。换句话说,iter只能指向vv的地一个元素,不能指向其他的元素。
但是这样的语句是正确的:
×iter = 10;

vector vv(10,9);
vector :: const_iterator iter;
即定义了一个const_iterator迭代器。这个const_iterator迭代器是可以自己增加的,但是其所指向的元素是不可以被改变的。比如说
for(iter = vv.begin(); iter != vv.end(); ++iter){
cout << *iter << endl;
}
这样是正确的,即iter本身的值是可以改变的。但是
for(iter = vv.begin(); iter != vv.end(); ++ iter){
*iter = 0;
}
这样是不对的,因为const_iterator迭代器是不能改变其所指向的元素的值的。

2、【vector】

vector 对象的 size
empty 和 size 操作类似于 string 的相关操作(3.2.3 节)。成员函数
size 返回相应 vector 类定义的 size_type 的值。
使用 size_type 类型时,必须指出该类型是在哪里定义的。
vector 类型总是包括总是包括 vector 的元素类型:
vector::size_type// ok
vector::size_type // error

// reset theelements in the vector to zero
for (vector::size_type ix = 0; ix != ivec.size(); ++ix)
ivec[ix] = 0;
【vector 下标操作不添加元素】
以下赋值是错误的
vectorivec; // empty vector
for (vector::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has no elements
正确的做法为:
vectorivec;
for(vector::size_typeix = 0; ix != 0; ++ix)
{ ivec.push_back(ix); }

3、用string 对象初始化 bitset 对象

【用string 对象初始化 bitset 对象】
string strval(“1100”);
bitset<32> bitvec4(strval);
当用 string 对象初始化 bitset 对象时,string 对象直接表示为位模式。
从 string 对象读入位集的顺序是从右向左(from right to left):
string 对象和 bitsets 对象之间是反向转化的:string 对象
的最右边字符(即下标最大的那个字符)用来初始化 bitset 对
象的低阶位(即下标为 0 的位)。当用 string 对象初始
化 bitset 对象时,记住这一差别很重要。

为了使程序更清晰、简短,容器类型最常用的构造函数是默认
构造函数。在大多数的程序中,使用默认构造函数能达到最佳
运行时性能,并且使容器更容易使用。

#

【C++primer 4th】
1、【引入 back_inserter】
插入迭代器
通常,用迭代器给容器元素赋值时,
被赋值的是迭代器所指向的元素。而使用插入迭代器赋值时,则会在容器中添加
一个新元素,其值等于赋值运算的右操作数的值。
fill_n (back_inserter(vec), 10, 0); // appends 10 elements to vec
fill_n(vec.begin(), 10, 0); //危险用法

2、【istream_iterator】
可使用 istream_iterator 对象将标准输入读到 vector 对
象中。
istream_iterator in_iter(cin); // read ints from cin
istream_iterator eof; // istream “end” iterator
while (in_iter != eof)
vec.push_back(*in_iter++);
【ostream_iterator】

#

【list容器】
对于 list 对象,应该优先使用 list 容器特有的成员
版本,而不是泛型算法
【类】
仅在类的私有部分定义数据成员,类的设计者就可以自由地修改数据。
如果实现改变了,那么只需检查类代码来了解此变化可能造成的影响。
如果数据为仅有的,则任何直接访问原有数据成员的函数都可能遭到破
坏。在程序可重新使用之前,有必要定位和重写依赖原有表示的那部分
代码。
同样地,如果类的内部状态是私有的,则数据成员的改变只可能在有限
的地方发生。避免数据中出现用户可能引入的错误。如果有缺陷会破坏
对象的状态,就在局部位置搜寻缺陷:如果数据是私有的,那么只有成
员函数可能对该错误负责。对错误的搜寻是有限的,从而大大方便了程
序的维护和修正。
class Screen {
public:
// interface member functions
typedef std::string::size_type index; //希望用户使用这个名字
private:
std::string contents;
index cursor;
index height, width;
};
【可变数据成员】
private:
mutable size_t access_ctr; // may change in a const members
【类作用域】
class Screen {
public:
typedef std::string::size_type index;
index get_cursor() const;
};
inline Screen::index Screen::get_cursor() const
{
return cursor;
}

#

【友元】
class Screen {
// Window_Mgr members can access private parts of class Screen
friend class Window_Mgr;
};
Window_Mgr 的成员可以直接引用 Screen 的私有成员。
class Screen {
// Window_Mgrmust be defined before class Screen
friend Window_Mgr&
Window_Mgr::relocate(Window_Mgr::index,
Window_Mgr::index,
590
Screen&);
// …restofthe Screen class
};
如果不是将整个 Window_Mgr 类设为友元,Screen 就可以指定只允许
relocate 成员访问:
【static 类成员】
static 成员是类的组成部分但不是任何对象的组成部分,因此,static 成
员函数没有 this 指针。通过使用非 static 成员显式或隐式地引用 this 是一
个编译时错误。
因为 static 成员不是任何对象的组成部分,所以 static 成员函数不能被
声明为 const。毕竟,将成员函数声明为 const 就是承诺不会修改该函数所属
的对象。最后,static 成员函数也不能被声明为虚函数。
static 数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,
static 成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。

#

【const static 成员】
类的 static 成员,像普通数据成员一样,不能在类的定义体中
初始化。相反,static 数据成员通常在定义时才初始化。
这个规则的一个例外是,只要初始化式是一个常量表达式,整型 const
static 数据成员就可以在类的定义体中进行初始化
class Account {
public:
static double rate() { return interestRate; }
static void rate(double); // sets a new rate
private:
static const int period = 30; // interest posted every 30 days
double daily_tbl[period]; // ok: period is constant expression
};
const static 数据成员在类的定义体中初始化时,该数据成员
仍必须在类的定义体之外进行定义。
在类内部提供初始化式时,成员的定义不必再指定初始值:
// definition of static member with no initializer;
// the initial value is specified inside the class definition
const int Account::period;
【static 成员不是类对象的组成部分】
例如,static 数据成员的类型可以是该成员所属的类类型。非 static 成
员被限定声明为其自身类对象的指针或引用:
class Bar {
public:
// …
private:
static Bar mem1; // ok
Bar *mem2; // ok
Bar mem3; // error
};
static 数据成员可用作默认实参:
class Screen {
public:
// bkground refers to the static member
// declared later in the class definition
Screen& clear(char = bkground);
private:
static const char bkground = ‘#’;
};
非 static 数据成员不能用作默认实参,因为它的值不能独立于所属的对象
而使用。使用非 static 数据成员作默认实参,将无法提供对象以获取该成员的
值,因而是错误的。

#

【合成复制构造函数】
class Sales_item {
// other members and constructors as before
private:
std::string isbn;
int units_sold;
double revenue;
};
合成复制构造函数如下所示:
Sales_item::Sales_item(const Sales_item &orig):
isbn(orig.isbn), // uses string copy constructor
units_sold(orig.units_sold), // copies orig.units_sold
revenue(orig.revenue) // copy orig.revenue
{ } // empty body
【禁止复制构造函数】
通过声明(但不定义)private 复制构造函数,可以禁止任何复制类
类型对象的尝试:用户代码中复制尝试将在编译时标记为错误,而成员函数和友
元中的复制尝试将在链接时导致错误。
【大多数类应定义复制构造函数和默认构造函数】
不定义复制构造函数和/或默认构造函数,会严重局限类的使用。不允许复
制的类对象只能作为引用传递给函数或从函数返回,它们也不能用作容器的元
素。
一般来说,最好显式或隐式定义默认构造函数和复制构
造函数。只有不存在其他构造函数时才合成默认构造函
数。如果定义了复制构造函数,也必须定义默认构造函
数。

#

1、封装(encapsulation)
一般而言,成员变量尽量声明为private,而成员函数则通常声明为public;
把数据声明为private,不允许外界随意存取,只能通过特定的接口(通常为成员函数)来操作,这就是面向对象的封装特性。
2、多态+虚函数
如果你以一个“基类之指针”指向一个“派生类之对象”,那么经由此指针,你就只能够调用基类(而不是派生类)所定义的函数。而虚函数正是为了对之前这条规则反其道而行之的设计。就是:你以一个“基类之指针”指向一个“派生类之对象”,但是此时的成员函数被声明为虚函数(virtual),则“基类之指针”所调用的虚成员函数,为各自“派生类之对象”的成员函数。(基类一般化,不做具体的实现,而具体的实习在各自的派生类中,而基类和派生类中都声明了同样的成员函数,此时声明的函数最好声明为虚函数,以解决多态的问题)
3、static_cast和reinterpret_cast
http://blog.csdn.net/deyili/article/details/5354242
4、this指针

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (!m_wndToolBar.CreateEx(**this**, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP 
            | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || 
            !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) 
        { 
            TRACE0("Failed to create toolbar\n"); 
            return -1;      // fail to create 
        } 
    m_newToolBar.EnableDocking(CBRS_ALIGN_ANY); 
    DockControlBar(&m_newToolBar); 
}

CreateEx中的this参数,指向CMainFrame对象。

你可能感兴趣的:(C++,vector,iterator,迭代器)