您的位置:首页 > 编程语言 > C语言/C++

C++学习杂笔(总)

2015-07-19 13:33 246 查看

私有构造函数

通常我们都将构造函数的声明置于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.最后,为了避免值复制时产生新的对象副本,除了将构造函数置为私有外,复制构造函数也要特别声明并置为私有。

虚函数&纯虚函数

首先:强调一个概念

定义一个函数为虚函数,不代表函数为不被实现的函数

定义他为虚函数是为了允许用基类的指针来调用子类的这个虚函数

定义一个函数为纯虚函数,才代表函数没有被实现。

定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。

声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例。

纯虚函数最显著的特征是:它们必须在继承类中重新声明函数(不要后面的=0,否则该派生类也不能实例化),而且它们在抽象类中往往没有定义。

定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。

纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。

虚函数和纯虚函数的区别

虚析构函数的作用

作为通常的原则,如果一个类定义了虚函数,那么它的析构函数就应当是virtual的。因为定义了虚函数则隐含着:这个类会被继承,并且会通过基类的指针指向子类对象,从而得到多态性。

虚析构函数的作用:当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

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

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

/article/3686520.html

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对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: