本帖最后由 hotpower 于 2012-9-22 13:20 编辑
John Lee<j.y.lee@yeah.net> 20:27:15
今天讨论,C++类的一般概念。
C++的类(class)是以C的结构(struct)为基础发展而来的。结构中的数据,在C++中称为“数据成员(data member)”。C++类与C结构之间的区别之一,是C++的类中可以定义函数,C++称为“成员函数(member function)”。在C程序中,struct中的数据都是公开的,程序中只要有一个struct的地址,就可以随便访问其中的数据。
雁塔菜农<hotwc3@qq.com> 20:29:25
实际只要会结构就应该会C++
John Lee<j.y.lee@yeah.net> 20:30:03
随着程序规模的增长,缺点就开始暴露、变大了,由于各个程序员的编程素质不同,某些程序员可能随意访问struct中的数据,造成了各模块之间的耦合性增大,最终,程序就变成了一团乱麻。
John Lee<j.y.lee@yeah.net> 20:30:37
C++的出现,在语法上就提供了限制这种随意性的机制,C++称为“数据隐藏”。
class c {
public:
...
private:
int data;
...
};
c类定义中出现的“private”就是针对数据隐藏的新语法(关键字)。
在private之后的的数据定义,都具有了“私有”访问属性。
小飞(467503326) 20:34:01
C语言中也可以有函数吧 结构体里面
John Lee<j.y.lee@yeah.net> 20:34:28
可以的。
雁塔菜农<hotwc3@qq.com> 20:34:49
应该是函数指针吧
John Lee<j.y.lee@yeah.net> 20:36:03
例如:int data; 就是私有的
你无法通过一个该类的实例的地址来访问这个成员。
例如:
c inst;
inst.data++; // 编译报错
雁塔菜农<hotwc3@qq.com> 20:38:09
安全
李冬发(632653918) 20:40:31
成员函数
John Lee<j.y.lee@yeah.net> 20:40:31
要访问这些“隐藏了”的数据,必须通过另一种方法:类的“成员函数”。
雁塔菜农<hotwc3@qq.com> 20:40:47
高级语言的属性一般都是通过get(),set()来访问
即只读或只写???限制其访问
John Lee<j.y.lee@yeah.net> 20:43:00
但是,这并不意味着可以随意访问“隐藏”的数据,因为“成员函数”是由类提供的一段逻辑实现(函数),是可以决定怎样访问“隐藏”的数据。
从某种意义上说,使用某个类的应用程序,除了知道这个类中有一个私有数据的名字以外,就没有什么别的了。
甚至,私有数据的名字,都可以改成无意义的标识符。
例如,look中的sync_t类:
class sync_t {
public:
sync_t() __OPT_ATTR__;
protected:
base::task_t* wakeup(uintptr_t data, base::task_t* task = 0) __OPT_ATTR__;
base::task_t* do_wakeup(uintptr_t data, base::task_t* task = 0) __OPT_ATTR__;
protected:
uintptr_t internal_data;
};
里面有一个internal_data
你除了能“看见”它之外,别的什么都不能干。
雁塔菜农<hotwc3@qq.com> 20:48:24
动不了它
它躲在类里
John Lee<j.y.lee@yeah.net> 20:49:44
访问修饰符是:protected。外面的程序不能访问它。
C++为类中的成员提供了3中“访问修饰符”。
public, private, protected。
各个修饰符按出现的先后顺序起作用。
雁塔菜农<hotwc3@qq.com> 20:53:16
这就是比C安全的地方
就和访问结构里的成员变量一样
John Lee<j.y.lee@yeah.net> 20:53:15
public修饰的“数据成员”和“成员函数”,外部程序可以随意访问。
程序中,总有一些数据是不希望别的“模块”来访问的。
private修饰的,除了类的“成员函数”外,不允许其他程序访问。
protected修饰的成员,可以被“派生类”访问,而不允许其他程序访问。
如果还不了解“派生”的概念,protected就先不管它。
class c {
public:
int get();
private:
int data;
};
int c::get()
{
return data;
}
这个就是一个非常简单的“成员函数”访问“私有”成员的例子。
int c::get()是 c 类的“成员函数”,int data是“私有的数据成员”。
根据上面描述的规则:成员函数可以访问私有数据,所以get()中是可以访问data的。
而get()本身又是一个public修饰的成员函数,能够被其他程序访问。
所以,从效果来看:“程序可以间接地读取data的值”。
但c类中没有提供“写”data的函数,所以data对程序来说,有“只读”的效果。
雁塔菜农<hotwc3@qq.com> 21:05:30
get()现在应该是只读的
John Lee<j.y.lee@yeah.net> 21:08:32
c inst1;
c inst2;
int val = inst1.get();
有两个c类的实例:inst1, inst2.
每个实例都应该有data数据成员。那么c::get()怎么知道应该读取哪一个实例的data?
在调用get之前,必须为其指明具体实例。
inst1.get();
或inst2.get();
我们知道,函数,其实就是一个地址,里面有代码。
而调用,就是把返回地址保存之后,跳转到函数的地址。
那么从调用的过程来看,get()是怎么知道应该访问哪个实例的data?
如果我们用C的方法,应该怎么办?
typedef struct {
int data;
} c;
int get(c* p)
{
return p->data;
}
c inst1;
c inst2;
int val = get(&inst1);
我们通过一个参数,来传递实例的地址。
get()函数里面,就很容易地访问对应的实例中的data了。
C++的方法完全与C一样。
只不过那个实例地址参数,被“隐含”了。
这个“隐含”的实例地址(指针),C++称为“this”。
this是C++的关键字。
除了this的“隐含”属性之外,所有的用法,与C的方法一样。
回到C++例子中:
int val = inst1.get();
编译器会使用inst1的地址作为this指针,调用c::get()函数。
在很多C程序中,可以看到某些函数的第1个参数,是一个struct的指针,而函数中,大部分也是对这个struct的成员进行操作,这个函数实际上就等于C++中的成员函数的作用。
“::”是C++的“作用域分辨运算符”。
表示::之后的成员的作用域限于::之前的类。
我们在C编程时,也有作用域的概念。
struct a {
int data;
};
struct b {
int data;
};
为了完整地表示data,必须在其之前加上作用域限定。
如:inst.data; p->data; 等等。
下面讲一点延伸的细节。
class c1 {
public:
int foo();
...
};
class c2 {
public:
int foo();
...
};
两个类:c1, c2 里都有成员函数 foo();
在C程序中,我们调用一个函数:
foo();
编译后的结果,对arm来说是:
bl foo
可能有的编译器会加上前置下划线:_
bl _foo
bl是arm指令。
对x86是:
call foo
6502是:
jsr foo
不管怎样,都会使用一个符号地址来表示调用的函数。
对C来说,函数不能同名,所以汇编的符号地址可以直接对应C的函数名。
但在C++上,就有麻烦了。
不同的类可以有同名的成员函数。
c1::foo();
c2::foo();
以后还会讲到的函数重载,都是同名函数。
那么如何映射到汇编的符号地址?
C++引人了一个概念:name mangling.
我以前看的书上称为“名字粉碎”
简单地说,汇编的符号地址,除了“函数名”外,还需要其他的元素来共同组成。
这些元素包括:名字空间名,类名,各个参数的类型等等。
最终组成的汇编符号,可能非常长。
回复John Lee<j.y.lee@yeah.net> 21:57:35
看见的部分,仅仅是函数名本身。
如果有模板(template),那么符号还会更加长,几百个字符都有可能的。
这个符号是为链接器(linker)准备的。
人看着基本都晕。
这里有一个实际的符号例子,给大家看看:
_ZN4look3edf12task_queue_t6insertERNS0_6task_tE
在这里讨论这个,只是告诉大家,编译器是可以区分同名函数的,不会搞错。 |