通常我们都将构造函数的声明置于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.最后,为了避免值复制时产生新的对象副本,除了将构造函数置为私有外,复制构造函数也要特别声明并置为私有。
————————————————————————————————————————————————————
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迭代器是不能改变其所指向的元素的值的。
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); }
【用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对象。