随笔 - 30, 文章 - 0, 评论 - 64, 引用 - 0
数据加载中……

2010年9月26日

Native C++ For WM系列

转http://www.cnblogs.com/longqi293/category/231183.html

posted @ 2010-09-26 15:01 henry08 阅读(265) | 评论 (0)编辑 收藏

十年MFC经历认识的Microsoft技术

十年MFC经历认识的Microsoft技术

posted @ 2010-09-26 13:39 henry08 阅读(272) | 评论 (0)编辑 收藏

2010年5月28日

控件使用

1: IDC_MyLable控件添中内容
   
    const char* str =  "ss" ;
    this->SetDlgItemTextW(IDC_MyLable, (LPCTSTR)str);

posted @ 2010-05-28 14:59 henry08 阅读(207) | 评论 (0)编辑 收藏

2010年5月19日

C语言、C++、VC电子资料(电子书+视频)下载汇总

C语言、C++、VC电子资料(电子书+视频)下载汇总

Visual C++ 6.0/Visual Studio 6.0下载:
Visual C++ 6.0 简体中文企业版 集成SP6完美版
Visual C++ 6.0 英文企业版 集成SP6完美版
Visual Studio 6.0 英文企业版 集成SP6完美版
C语言:
谭浩强C语言教程全书 Word版
C语言趣味程序百例精解
Sams出版 Advanced C 高级C语言开发
C数值算法程序大全
C语言专家编程
C语言程序设计案例精编
C语言进阶
C Primer Plus [SAMS]
C Pocket Reference [O'Reilly]
Practical C Programming
The C Programming Language
 
吉林大学C语言视频教程 全51集 CSF格式
C语言基础视频教程 全14讲 完整上架
C语言程序设计视频教程 曾怡教授讲解 全28讲完整版下载
数据结构视频教程 清华大学严蔚敏主讲 全48讲 完整版 ASF格式

数据结构C语言版视频教程 全52讲完整版
C++:
钱能 c++程序设计教程 第二版 高清PDF版

Essential C++ 中文版+英文版 侯捷译
编程点金之Visual C++(电子教程) iso
C++程序调试实用手册
C++面向对象多线程编程
C++高级编程参考手册
C++ Primer 第三版
C++ Primer 第四版 中文版高清晰
C和C++代码精粹
C++沉思录
 
高质量C++编程指南
C++ 编程思想 第1卷 标准C++导引
C++ 编程思想 第2卷 实用编程技术
 
More Effective C++ 简体中文版
C++ Templates 中文版+英文版
C++ Coding Standards[Addison Wesley]
Absolute C++ 全彩页代码剖析
Accelerated C++ - Practical Programming by Example
 
Inside the C++ Object Model
More Exceptional C++ [Addison Wesley]
Exceptional C++ [Addison Wesley]
Sams出版 C++ Unleashed
C++ GUI Programming with QT4
VC++深入详解 孙鑫作品系列 高清PDF版下载
Visual C++ 6傻瓜书 快速参考
Visual C++ 6.0 编程实例与技巧(PDF中文版)
Visual C++ 21天自学教程
跟我学Visual C++ 6
Visual C++ 6 数据库编程 21天自学教程 CHM
The C++ Programming Language 第三版
深度探索C++对象模型
Special Edition Using Visual C++ 6
Professional MFC with VC 6
Practical C++ Programming
Cross-Platform Development in C++
Iterative UML Development Using VC++ 6
Ace Programmer's Guide
Visual C++高级界面特效制作百例
精通GDI+编程 清华大学出版
Borland C++ Builder 6 开发指南 CHM
孙鑫C++视频教程 rmvb格式 全20CD完整版
C++程序设计视频教程 东南大学何洁月主讲 全80讲(上) 48讲
C++程序设计视频教程 东南大学何洁月主讲 全80讲(下) 32讲
C++视频教程 边用边学Visual C++ 6 (ASF格式)
中山大学蔡培兴 C++语言视频教程 全51讲 精品推荐

posted @ 2010-05-19 09:36 henry08 阅读(719) | 评论 (0)编辑 收藏

孙鑫video

http://www.sunxin.org/video/index.htm

posted @ 2010-05-19 09:16 henry08 阅读(410) | 评论 (0)编辑 收藏

2010年2月26日

VC精华教程:MFC入门系列

VC精华教程:MFC入门系列(一)

posted @ 2010-02-26 16:08 henry08 阅读(562) | 评论 (1)编辑 收藏

2010年1月27日

vc.net中 子窗体如何调用父窗体中的控件



我有个Button1、Button2在From1中,另外有个Button3在Form2中
现在我在button1_Click中加入Form2^ Form2Dlg=gcnew Form2(); Form2Dlg->ShowDialog(this);
现在我想显示Form2后我按Button3后能响应Button2的Click事件怎么搞了?

posted @ 2010-01-27 17:15 henry08 阅读(545) | 评论 (0)编辑 收藏

2008年12月9日

从Java到C++ — 对比Java与C++编程的不同

1. 数据类型和变量

C++ 中的变量类型与Java很相似。像Java一样,C++ 有intdouble 类型。但是这些数字类型的取值范围是依赖于机器的。比如在16位系统上,例如运行DOS 或Windows 3.x的PC机上,int 是双字节(2-byte)的,取值范围比Java的4-byte的int 要小很多。在这些机器上,如果 int 不够用的话,你需要使用长整型long。

C++ 有 shortunsigned 类型来更有效的存储数字。(我认为所谓有效是指更高的空间利用率。) 最好是尽量避免使用这些类型除非是空间利用的有效性对你的系统真的非常重要。

在C++中布尔型用 bool 表示,而不像在Java中用boolean。

C++ 中字符串类型用 string 表示。它与Java中的 String 类型非常相似,但是,还是要逐一以下几点不同之处:

1. C++ 字符串存储ASCII 码字符,而不是标准码Unicode 字符

2. C++ 字符串是可以被修改的,而Java字符串的内容是不可修改的(immutable)。

3. 取子字符串的操作在 C++ 中叫做 substr,这个命令s.substr(i, n) 从字符串s中取得从位置 i 开始长度为n的子字符串。

4. 在C++中,你只能够将字符串与其它字符串对象相串联(concatenate),而不能够与任意的对象相串联。

5. C++中可以直接使用关系操作符 ==、 !=、 <、 <=、 >、 >= 来进行字符串比较,其中后面四个操作符是按字母顺序进行比较的。 这比Java中使用函数equals和compareTo来比较要方便很多。



2. 变量和常量

在C++中,本地变量的定义看起来与Java中相同,例如:

int n = 5;

实际上这正是C++和Java的一个重要不同之处。C++编译器不对本地变量进行初始化检验,所以在C++中很容易忘记初始化一个变量,这种情况下,变量的值该变量所占内存区域中刚好当前存在随机值。这显然是很容易产生程序出错的地方。

与Java一样, C++中类可以有数据域和静态变量。不同的是,C++中变量可以在函数甚至是类的外面定义,这些所谓的全局变量可以在程序的任何函数中被访问,因而不易被很好的管理。所C++中应该尽量避免使用全局变量。

在C++中,常量可以在任何地方被定义(记得在Java中,常量必须是类的静态数据static data)。 C++ 使用关键字 const 来定义常量,而Java中是 final。例如:

const int DAYS_PER_YEAR = 365;




3. 类

C++ 中对类的定义与Java有些不同,这里是一个例子:一个C++ 版本的 Point 类:

class Point /* C++ */

{

public:

Point();

Point(double xval, double yval);

void move(double dx, double dy);

double getX() const;

double getY() const;

private:

double x;

double y;

};

这里几点重要的不同是:

1. C++的类定义中分为公共和私有部分,分别以关键字 publicprivate开始。而在Java中,每一个元素都必须标明 publicprivate

2. C++中类的定义只包含函数的声明,真正的实现另外单独列出。

3. 访问函数(accessor methods)标有关键字 const ,表明这个函数不会改变本对象的元素值。

4. 类定义的结尾处有分号

类中函数的实现跟在类的定义之后。因为函数是在类外面定义的,所以每一个函数的名字前面要加类名称作为前缀,并使用操作符双冒号::来分割类的名称和函数的名称。不改变隐含参数值(即当前对象的值)的访问函数用 const标明。如下所示是上面类定义中的函数的实现:

Point::Point() { x = 0; y = 0; }

void Point::move(double dx, double dy)

{

x = x + dx;

y = y + dy;

}

double Point::getX() const

{

return x;

}




4. 对象

Java 与 C++ 最主要的不同在于对象变量的使用。在 C++中,对象变量存储的是真正的对象的值,而不是对象引用(reference)。注意在C++中构造一个对象的时候是不使用关键字new的,只需要在变量的名字后面直接赋予构造函数的参数就可以了,例如:

Point p(1, 2); /* 构造对象 p */

如果不跟参数赋值,则使用默认构造函数,例如:

Time now; /* 默认使用构造函数 Time::Time() */

这一点与Java很不同。在Java中,这个命令仅仅生成一个没有初始化的reference,而在C++中,它生成一个实际的对象。

当一个对象被赋给另一个对象变量的时候,实际的值将被拷贝。而在Java中,拷贝一个对象变量只不过是建立了另外一个指向对象的reference。拷贝一个C++的对象就像在Java中调用clone这个函数一样,而修改拷贝的值不会改变原对象的值。例如:

Point q = p; /* 拷贝p到q */

q.move(1, 1); /* 移动q而p不动,即q的值变了,而p的不变*/

多数情况下,C++中这种对象直接对值操作的特性使用起来很方便,但是也有些时候不尽如人意:

1. 当需要一个函数中修改一个对象的值,必须记住要使用按引用调用call by reference (参见下面函数部分)

2. 两个对象变量不能指向同一个对象实体。如果你要在C++中实现这种效果,必须使用指针pointer(参见下面指针部分)

3. 一个对象变量只能存储一种特定的类型的值,如果你想要使用一个变量来存储不同子类的对象的值(多态ploymorphism),则需要使用指针。

4. 如果你想在C++中使用一个变量来或者指向null或者指向一个实际的对象,则需要使用指针






5. 函数

在Java中,每一个函数必须或者是对象函数(instance method),或者是静态函数(static function)或称类函数。C++同样支持对象函数和静态函数(类函数),但同时C++也允许定义不属于任何类的函数,这些函数叫做全局函数(global functions)

特别的是,每一个C++ 程序都从一个叫做 main的全局函数开始执行:

int main()

{ . . .

}

还有另外一个格式的main函数可以用来捕捉命令行参数,类似于Java的main函数,但是它要求关于C格式的数组和字符串的知识,这里就不介绍了。

按照习惯,通常如果程序执行成功, main 函数返回0,否则返回非零整数。

同Java一样,函数参数是通过值传递的(passed by value)。在Java中,函数无论如何都是可以修改对象的值的。然而在C++中,因为对象直接存储的是实际的值,而不是指向值的reference,也就是说传入函数的是一个实际值的拷贝,因此也就无法修改原来对象的值。

所以,C++ 有两种参数传递机制,同Java一样的按值调用(call by value) ,以及按地址调用(call by reference)。当一个参数是按reference传递时,函数可以修改其原始值。Call by reference 的参数前面有一个地址号 & 跟在参数类型的后面,例如:

void raiseSalary(Employee& e, double by)

{ . . .

}

下面是一个典型的利用call by reference的函数,在Java中是无法实现这样的功能的。

void swap(int& a, int& b)

{ int temp = a;

a = b;

b = temp;

}

如果使用 swap(x, y)来调用这个函数,则reference参数 ab 指向原实际参数xy的位置,而不是它们的值的拷贝,因此这个函数可以实现实际交换这两个参数的值。

在 C++中,每当需要实现修改原参数的值时你就可以使用按地址调用 call by reference




6. 向量Vector

C++ 的向量结构结合了Java中数组和向量两者的优点。一个C++ 的向量可以方便的被访问,其容量又可以动态的增长。如果 T 是任意类型,则 vector<T> 是一个元素为 T 类型的动态数组。下面的语句

vector<int> a;

产生一个初始为空的向量。而语句

vector<int> a(100);

生成一个初始有100个元素的向量。你可以使用push_back 函数来添加元素:

a.push_back(n);

调用 a.pop_back()a中取出最后一个元素(操作后这个元素被从a中删掉), 使用函数size 可以得到当前a中的元素个数。

你还可以通过我们熟悉的 [] 操作符来访问向量中元素,例如:

for (i = 0; i < a.size(); i++) {

sum = sum + a[i];

}

同Java中一样,数组索引必须为 0 和 a.size() - 1之间的值。但是与Java不同的是,C++中没有runtime的索引号合法性检验。试图访问非法的索引位置可能造成非常严重的出错。

就像所有其它 C++ 对象一样,向量也是值。如果你将一个向量赋值给另外一个向量变量,所有的元素都会被拷贝过去。

vector<int> b = a; /* 所有的元素都被拷贝了 */

对比Java中的情况,在Java中,一个数组变量是一个指向数组的reference。拷贝这个变量仅仅产生另外一个指向同一数组的reference,而不会拷贝每一个元素的值。

正因如此,如果一个C++函数要实现修改向量的值,必须使用reference参数:

void sort(vector<int>& a)

{ . . .

}




7. 输入和输出

在C++中,标准的输入输出流用对象 cincout 表示。我们使用 << 操作符写输出,例如:

cout << “Hello, World!”;

也可以连着输出多项内容,例如:

cout << “The answer is ” << x << “\n”;

我们使用 >> 操作符来读入一个数字或单词,例如:

double x;

cout << “Please enter x: “;

cin >> x;

string fname;

cout << “Please enter your first name: “;

cin >> fname;

函数getline 可以读入整行的输入,例如:

string inputLine;

getline(cin, inputLine);

如果到达输入的结尾,或者一个数字无法被正确的读入,这个流对象会被设置为 failed 状态,我们可以使用函数 fail 来检验这个状态,例如:

int n;

cin >> n;

if (cin.fail()) cout << “Bad input”;

一旦一个流的状态被设为failed,我们是很难重置它的状态的,所以如果你的程序需要处理错误输入的情况,应该使用函数 getline 然后人工处理得到的输入数据。

 

 



8. 指针pointer

我们已经知道在C++中,对象变量直接存储的是对象的值。这是与Java不同的,在Java中对象变量存储的是一个地址,该地址指向对象值实际存储的地方。有时在C++中也需要实现这样的布置,这就用到了指针pointer。在 C++中,一个指向对象的变量叫做指针。如果T是一种数据类型,则 T* 是指向这种数据类型的指针。

就像 Java中一样,一个指针变量可以被初始化为空值 NULL,另外一个指针变量的值,或者一个调用new生成的新对象:

Employee* p = NULL;

Employee* q = new Employee(”Hacker, Harry”, 35000);

Employee* r = q;

实际上在C++中还有第四种可能,那就是指针可以被初始化为另外一个对象的地址,这需要使用地址操作符 &

Employee boss(”Morris, Melinda”, 83000);

Employee* s = &boss;

这实际上并不是什么好主意。保险的做法还是应该直接让指针指向使用 new生成的新对象。

到目前为止,C++ 指针看起来非常像 Java 的对象变量。然而,这里有一个很重要的语法的不同。我们必须使用星号操作符 * 来访问指针指向的对象。如果 p 是一个指向Employee对象的指针,则 *p 才代表了这个对象:

Employee* p = . . .;

Employee boss = *p;

当我们需要执行对象的函数或访问对象的一个数据域时,也需要使用 *p

(*p).setSalary(91000);

*p外面的括号是必需的,因为 . 操作符比 * 操作符有更高的优先级。C的设计者觉得这种写法很难看,所以他们提供了另外一种替代的写法,使用 -> 操作符来实现 *. 操作符的组合功能。表达式

p->setSalary(91000);

可以调用对象*p的函数 setSalary 。你可以简单的记住 . 操作符是在对象上使用的,-> 操作符是在指针上使用的。

如果你不初始化一个指针,或者如果一个指针为空值 NULL 或指向的对象不再存在,则在它上面使用 *-> 操作符就会出错。 不幸的是 C++ runtime 系统并不检查这个出错。如果你范了这个错误,你的程序可能会行为古怪或死机。

而在Java中,这些错误是不会发生的。所有的reference都必须初始化,所有的对象只要仍有reference指向它就不会被从内存中清除,因此你也不会有一个指向已被删除的对象的reference。Java的runtime 系统会检查reference是否为空,并在遇到空指针时抛出一个null pointer的例外(exception)。

C++ 和 Java还有一个显著的不同,就是 Java 有垃圾回收功能,能够自动回收被废弃的对象。而在C++中,需要程序员自己管理内存分配回收。

C++中当对象变量超出范围时可以自动被回收。但是使用new生成的对象必须用delete操作符手动删除,例如:

Employee* p = new Employee(”Hacker, Harry”, 38000);

. . .

delete p; /* 不在需要这个对象 */

如果你忘记删除一个对象,那么你的程序有可能最终用光所有内存。这就是我们常说的内存泄漏 (memory leak)。更重要的是,如果你如果删除了一个对象,然后又继续使用它,你可能覆盖不属于你的数据。如果你刚巧覆盖了用于处理内存回收的数据域,那么内存分配机制就可能运转失常而造成更严重的错误,而且很难诊断和修复。因此,在C++中最好尽量少用指针




9. 继承

C++和Java中继承的基本语法是很相似的。在C++中,使用 : public 代替Java中的extends 来表示继承关系 。 (C++ 也支持私有继承的概念,但是不太有用。)

默认情况下,C++中的函数不是动态绑定的。如果你需要某个函数实现动态绑定,需要使用virtual声明它为虚函数,例如:

class Manager : public Employee

{

public:

Manager(string name, double salary, string dept);

virtual void print() const;

private:

string department;

};

同Java一样,构造函数中调用父类的构造函数有特殊的语法。 Java使用关键字 super。C++中必须在子类的构造函数体外调用父类的构造函数。下面是一个例子:

Manager::Manager(string name, double salary, string dept)

: Employee(name, salary) /* 调用父类的构造函数 */

{ department = dept;

}

Java 中在子类函数中调用父类的函数时也使用关键字 super 。而在C++中是使用父类的名称加上操作符 ::表示,例如:

void Manager::print() const

{ Employee::print(); /* 调用父类的函数 */

cout << department << “\n”;

}

一个 C++ 对象变量只能存储特定类型的对象值。要想在C++中实现多态(polymorphism),必须使用指针。一个 T* 指针可以指向类型为 TT 的任意子类的对象,例如:

Employee* e = new Manager(”Morris, Melinda”, 83000, “Finance”);

你可以将父类和不同子类的对象混合收集到一个元素均为指针的向量中,然后调用动态绑定的函数,如下所示:

vector<Employee*> staff;

. . .

for (i = 0; i < staff.size(); i++)

staff[i]->print();

posted @ 2008-12-09 13:43 henry08 阅读(4256) | 评论 (32)编辑 收藏

正确的方法是定义operator++以reference为参数类型 C++中Reference与指针(Pointer)的使用对比

day &operator++(day &d)
{
d = (day)(d + 1);
return d;
}

使用这个函数, 表达式 ++x 才有正确的显示以及正确的操作。
Passing by reference不仅仅是写operator++较好的方法,而是唯一的方法。


 C++在这里并没有给我们选择的余地。
 像下面的声明:
day *operator++(day *d);
是不能 通过编译的。
每个重载的操作符函数必须或者是一个类的成员, 或者使用类型T、 T & 或 T const & 为参数类型,
这里T是一个类(class)或列举(enumeration)类型。

也就是说,每一个重载操作符必须以类或列举类型为参数类型。

指针,即使是指向一个类或列举类型对象的指针,也不可以用。

C++ 不允许在重载操作符时重新定义内置操作符的含义,包括指针类型。
因此,我们不可以定义:
int operator++(int i); // 错误
因为它试图对int重新定义操作符 ++ 的含义。 我们也不可以定义:
int *operator++(int *i); // 错误
因为它试图对 int * 重新定义操作符 ++ 的含义

 

 

References vs. const pointers

C++ 中不允许定义”const reference”,
 因为一个reference天生就是const。也就是说,一旦将一个reference绑定到一个对象,就无法再将它重新绑定到另一个不同的对象。
在声 明一个reference之后没有写法可以将它重新绑定到另外一个对象。
例如:
int &ri = i;
将 ri 绑定到 i 。然后下面的赋值:
ri = j;
并不是把 ri 绑定到 j ,而是将 j 中的值赋给 ri 指向的对象,也就是赋给 i 。



简而言之,
一个pointer在它的有生之年可以指向许多不同的对象,
而一个reference只能够指向一个对象。
有些人认为这才是 reference和 pointer最大的不同。
我并不赞成。也许这是reference与pointer的一点不同, 但并不是reference和const pointer的不同。
在强调一遍,一旦一个reference与一个对象绑定,就不能再将它改指向另外的东西。
既然不能再绑定reference之后再 改变, 一个reference就必须在一出生就被绑定。
否则这个reference就永远不能被绑定到任何东西,也就毫无用处了。

上一段的讨论也同样完全适用于常量指针(const pointer)。
(注意,我这里说的是常量指针(const pointer), 而不是指向常量的指针 “pointers to const”。)
 例如,
一个reference声明必须同时带有一个初始化赋值,如下所示:

void f()
{
int &r = i;

}

省略这个初始化赋值将产生一个编译错误:

void f()
{
int &r; //错误

}

一个常量指针的声明也同样必须带有一个初始化赋值,如下所示:

void f()
{
int *const p = &i;

}

省略这个初始化赋值同样会出错:

void f(){
int *const p; // 错误

}

在我看来
不能够对reference二次绑定作为reference与pointer的不同。
并不比常量指针和非常量指针的不同更为显著。




Null references

除了显示的不同,常量指针与reference还有一点非常不同,那就是,一个有效的reference必须指向一个对象;而一个指针不需要。一个指针,即使是一个常量指针, 都可以有空值。 一个空指针不指向任何东西。

这点不同就暗示当你想要确信一个参数必须指向一个对象的时候,应该使用reference作为参数类型。 例如,交换函数(swap function),它接受两个int参数,并将两个参数的数值对调,如下所示:

int i, j;
swap(i, j);

将原本在 i 中的值放到 j 中, 并将原本在 j 中的值放到 i 中。我们可以这样写这个函数:

void swap(int *v1, int *v2)
{
int temp = *v1;
*v1 = *v2;
*v2 = temp;
}

这种定义下,函数要像这样被调用: swap(&i, &j);

这个接口暗示其中一个或两个参数都有可能为空(null)。而这个暗示是误导的。例如,调用
swap(&i, NULL);
的后果很可能是不愉快的。

而像下面这样定义reference为参数:

void swap(int &v1, int &v2)
{
int temp = v1;
v1 = v2;
v2 = temp;
}

清晰的表明了调用swap应该提供两个对象,它们的值将被交换。 并且这样定义的另一个好处是,在调用这个函数的时候,不需要使用那些&符号,看起来更顺眼:
swap(i, j);








 

Null references

除了显示的不同,
常量指针与reference还有一点非常不同,
那就是,一个有效的reference必须指向一个对象;

一个指针不需要
一个指针,即使是一个常量指针, 都可以有空值。 一个空指针不指向任何东西。

这点不同就暗示当你想要确信一个参数必须指向一个对象的时候,应该使用reference作为参数类型。
 例如,
交换函数(swap function),它接受两个int参数,并将两个参数的数值对调,如下所示:

int i, j;
swap(i, j);

将原本在 i 中的值放到 j 中, 并将原本在 j 中的值放到 i 中。我们可以这样写这个函数:

void swap(int *v1, int *v2)
{
int temp = *v1;
*v1 = *v2;
*v2 = temp;
}

这种定义下,函数要像这样被调用: swap(&i, &j);

这个接口暗示其中一个或两个参数都有可能为空(null)。而这个暗示是误导的。例如,调用
swap(&i, NULL);
的后果很可能是不愉快的。

而像下面这样定义reference为参数:

void swap(int &v1, int &v2)
{
int temp = v1;
v1 = v2;
v2 = temp;
}

清晰的表明了调用swap应该提供两个对象,它们的值将被交换。 并且这样定义的另一个好处是,在调用这个函数的时候,不需要使用那些&符号,看起来更顺眼:
swap(i, j);


更安全?


有些人认为既然reference不能够为空,那么它应该比指针更安全。
 我认为reference可能要安全一点,但不会安全很多。
虽然一个有效的reference不能为空,但是无效的可以呀。
实际上,在很多情况下程序有可 能产生无效的reference,而不只是空的reference。

 例如,
你可以定义一个reference,使它绑定到一个指针指向的对象,如下所示:

int *p;

int &r = *p;

如果指针*p在reference定义时刚好为空,则这个reference为空。
 从技术上来说,这个错误并不在于将reference绑定到一个空值,而是在于对一个空指针去参考。
 对一个空指针去参考产生了一个不确定的操作,也就意味着很多事都可能发生,而且大部分都不是什么好事。很有可能当程序将reference r 绑定到*p (p所指向的对象)的时候,p实际上没有被去参考,甚至程序只是将p的值拷贝给实现r的指针。
而程序将会继续执行下去直到错误在后面的运行中更为明显的表 现出来,产生不可预知的危害。

下面的函数
展示了
另外一种产生无效reference的方法:

int &f()
{
int i;

return i;
}

这个函数返回一个指向本地变量 i 的reference。
然而当函数返回时,本地变量 i 的存储空间也就消失了。因此这个函数实际返回了一个指向被回收了的空间的reference。这个操作与返回一个指向本地变量的指针的后果相同。
有些编译 器可以在编译时发现这个错误,但也很有可能不会发现。




我喜欢reference,也有很好的理由使用它们代替pointer。

但如果你期望使用reference来使你的程序健壮性显著增强,那么你多半会失望的



参考资料:

  1. Saks, Dan. “Introduction to References,” Embedded Systems Programming, January 2001, p. 81.
  2. Saks, Dan. “References and const“, Embedded Systems Programming February 2001, p. 73.

posted @ 2008-12-09 13:16 henry08 阅读(2122) | 评论 (2)编辑 收藏

2008年12月8日

VC6.0中重载操作符函数无法访问类的私有成员

在 C++ 中,操作符(运算符)可以被重载以改写其实际操作。
同时我们可以定义一个函数为类的朋友函数(friend function)以便使得这个函数能够访问类的私有成员,
这个定义通常在头文件中完成。

在Visual C++中定义一般的函数为朋友函数通常是没有问题的。
然而对某些重载操作符的函数,
即使我们将它们定义为类的朋友函数,VC的编译器仍然会显示出错信息,
认为这些朋友函数无权访问类的私有成员。
我认为这应该是VC6.0的bug。

以下代码就是个例子:

// 头文件 “Sample.h”
            #include<iostream>
            using namespace std;
            class Sample {
            public:
            Sample();
            friend ostream &operator<<(ostream &out, const Sample s);
            friend istream &operator>>(istream &in, Sample & s);
            private:
            int x;
            };

// 实现文件 “Sample.cpp”
            #include “Sample.h”
            Sample::Sample() {
            x=0;
            }
            istream &operator>>(istream &in, Sample & s) {
            cout<<”Please enter a value”<<endl;
            in >> s.x ;
            return in;
            }
            ostream &operator<<(ostream &out, const Sample s) {
            cout << s.x << endl;
            return out;
            }

以上代码在gnuc++中编译运行毫无问题。但是在VC++6.0中编译的时候就会出现以下的编译错误:

Compiling…
Sample.cpp
c:\temp\sample.cpp(8) : error C2248: ‘x’ : cannot access private member declared in class ‘Sample’
c:\temp\sample.h(19) : see declaration of ‘x’
c:\temp\sample.cpp(13) : error C2248: ‘x’ : cannot access private member declared in class ‘Sample’
c:\temp\sample.h(19) : see declaration of ‘x’
Error executing cl.exe.Sample.obj - 2 error(s), 0 warning(s)

在VC++ 6.0中解决这个问题有以下几种方法:

  • 在头文件中实现作为朋友函数的操作符函数的重载,也就是说在实现文件”Sample.cpp”中将函数重载的实现去掉,而将头文件修改如下:
    // 修改后的头文件 1 “Sample.h”
                    #include<iostream>
                    using namespace std;
                    class Sample {
                    public:
                    Sample();
                    friend ostream &operator<<(ostream &out, const Sample s);
                    friend ostream &operator<<(ostream &out, const Sample s) {
                    cout << s.x << endl;
                    return out;
                    }
                    friend istream &operator>>(istream &in, Sample & s);
                    friend istream &operator>>(istream &in, Sample & s) {
                    cout<<”Please enter a value”<<endl;
                    in >> s.x ;
                    return in;
                    }
                    private:
                    int x;
                    };
    
        
  • 在头文件中类定义之前将类和朋友操作符函数的原型特别声明一下,也就是将头文件修改如下(实现文件”Sample.cpp”不用作任何修改):
    // 修改后的头文件 2 “Sample.h”
                    #include<iostream>
                    using namespace std;
                    // 以下3行代码为新加入
                    class Sample;
                    ostream &operator<<(ostream &out, const Sample s);
                    istream &operator>>(istream &in, Sample & s);
                    class Sample {
                    public:
                    Sample();
                    friend ostream &operator<<(ostream &out, const Sample s);
                    friend istream &operator>>(istream &in, Sample & s);
                    private:
                    int x;
                    };
    
        
  • 第三种方法是对I/O名空间的使用实行明确声明,也就是说在头文件”Sample.h”中直接写:
    #include<iostream>
    using std::ostream;
    using std::istream
    ….
    取代 “using namespace std;”
    注意:在这个例子里我们在实现文件 “Sample.cpp”中包含 “using namespace std;”这句话,否则在实现中就不能使用 “cout” , “cin”, “<< “, “>>” 和 endl 这些关键字和符号。修改后的完整代码如下:

     

    // Sample.h
                    #include<iostream>
                    using std::istream;
                    using std::ostream;
                    class Sample {
                    public:
                    Sample();
                    friend ostream &operator<<(ostream &out, const Sample s);
                    /*friend ostream &operator<<(ostream &out, const Sample s) {
                    cout << s.x << endl;
                    return out;
                    }*/
                    friend istream &operator>>(istream &in, Sample & s);
                    /*friend istream &operator>>(istream &in, Sample & s) {
                    cout<<”Please enter a value”<<endl;
                    in >> s.x ;
                    return in;
                    }*/
                    private:
                    int x;
                    };
    // “Sample.cpp”
                    #include “Sample.h”
                    using namespace std;
                    Sample::Sample() {
                    x=5;
                    }
                    istream &operator>>(istream &in, Sample & s) {
                    cout<<”Please enter a value”<<endl;
                    in >> s.x ;
                    return in;
                    }
                    ostream &operator<<(ostream &out, const Sample s) {
                    cout << s.x << endl;
                    return out;
                    }
    
        

posted @ 2008-12-08 23:50 henry08 阅读(2318) | 评论 (5)编辑 收藏