积木

No sub title

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  140 Posts :: 1 Stories :: 11 Comments :: 0 Trackbacks

常用链接

留言簿(1)

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜

#

原文出自:http://www.wuzesheng.com/?p=840

C++中内存的动态分配与管理永远是一个让C++开发者头痛的问题,本文通过对C++中内存的动态分配释放的基本原理的介绍,让读者朋友能对C++中的内存的动态分配与释放有较为深入的理解,从而更好驾驭C++程序。

1. 函数(Function)
(1) operator new function

1
2
void * ::operator new(size_t);	                //Global
void * class-name::operator new(size_t); 	//Class


上面是C++中operator new function的原型,一个是全局类型的,一个的类成员类型的。全局类型的operator new函数在下面两种情况下被调用:一种是在分配C++内建(built-in)类型的动态内存时,一种是在分配用户没有自己定义operator new成员函数的用户自定义类型的动态内存时。 如果用户在自己定义的类型中,定义了operator new函数,那么用户在用new申请该类型的动态内存时, 便会调用该类型的成员函数operator new, 而不是全局的operator new。

另外,我们注意到,上面的原型中函数的返回值为void *类型, 第一个参数为size_t类型,这个是C++编译器要求的,如果要自己重载operator new函数,返回值必须为void* 类型,第一个参数必须为size_t类型,否则,编译器会返回如下错误信息:

1
error: ‘operator new’ takes type ‘size_t’ (‘unsigned int’) as first parameter

这里需要注意的一点是,我们可以利用operator new function可以重载的特点,可以通过参数传入一些额外的信息,来调试程序,检测内存泄露等。比如,我们可以像下面这样重载,传入调用处的行号,函数名,这样就可以跟踪内存的分配使用情况:

1
2
3
4
5
void * operator new(size_t unSize, int nLine, const char * pFunc)
{
    prinft("Line: %d, Func: %s, allocate %u byte(s)\n", nLine, pFunc, unSize);
    return malloc(unSize);
}

(2) operator delete function

1
2
void operator delete( void * );
void operator delete( void *, size_t );

上面是operator delete function的原型。operator delete function也有全局的和类成员的两种。这里需要注意,一个类只能有一个operator delete function做为其成员函数,而且必须为上面两种中的其中一种,没有其它的形式。如果一个类实现了自己的operator delete function成员函数,那么在释放该类型的内存时,编译器便会调用成员operator delete function, 而不是全局的。

上面的两种原型,第一种,在调用的时候,编译器会把要释放的内存的首地址传入,第二种,在调用的时候,编译器会把要释放的内存的首地址和大小都传入。因此,可以利用这一特性,如果我们在基类中实现第二种形式的operator delete function的成员函数,那么便可以用之来释放子类类型的内存(具体参考最后面的例子)。

2. 运算符(Operator)
(1) new operator

1
2
[::] new [placement] new-type-name [new-initializer]
[::] new [placement] ( type-name ) [new-initializer]

注:上面的’[]‘表示在其中的部分是optional(可选的)
上面是new operator的原型。在C++中,动态内存的分配,通常都是调用new operator来完成的,利用new operator来分配动态内存,编译器要做下面两项工作:

  • a. 调用operator new function分配内存(allocate the memory)
  • b. 调用构造函数(call the constructor)来进行初始化

下面来说一说new operator的原型中各部分到底是干什么的:
placement: 如果你重载了operator new function, placement可以用来传递额外的参数
type-name: 指定要分配的内存的类型,可以是内建(built-in)类型,也可以是用户自定义类型
new-initializer: 指定对分配后内存的初始化的参数,也就的构造函数的参数 。这里需要注意一点,在分配一个对象的数组类型的内存时,不能够指定初始化参数;换言之,要想分配一个对象的数组类型的内存,该对象必须有缺省构造函数

(2) delete opeartor

1
2
[::] delete cast-expression
[::] delete [ ] cast-expression

上面是delete operator的原型,第一种用来释放普通的对象(包括内建类型)类型的内存,第二种用来释放对象的数组类型的内存。在C++中,用new operator分配的动态内存,必须调用delete operator来释放,通常用delete operator释放内存编译器要做下面两项工作:

  • a. 调用对象析构函数来析构对象
  • b. 调用operator delete function来释放内存(deallocate the memory)


3. 关于new/delete使用过程中一些需要注意的点
(1)如何区别operator new/delete function 与 new/delete operator ?
通过上面的讲述,不难看出,我们分配/释放动态内存,调用的是new/delete operator, 而在调用new/delete的过程中,编译器会自动调用operator new/delete function来完成实际的内存分配/释放的工作

(2) 用delete operator去释放一块不是由new operator释放的内存,结果是不可预料的,因此,切记,operator new与operator delete一定要配对使用,这是写好程序的基础

(3) new operator调用失败会抛出std::bad_alloc异常,前提是你没有自己重载对应的operator new function;delete operator失败,常见的原因是多次delete同一块内存

(4) 如果一块内存被delete后,再对它解引用(Dereference),结果也是不可预测的,很可能导致程序崩溃

(5) delete一个空(NULL)指针是安全的,没有任何害处的

(6) 类成员类型的operator new/delete函数必须为静态(static)函数,因此它们不能为虚函数(virtual function),也遵守public, protected, private的访问权限控制

4. 关于上面所讲的内容的一些例子:
程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio .h>
#include <stdlib .h>
 
void * operator new(size_t unSize)
{
    printf("operator new called\n");
    return malloc(unSize);
}
 
void * operator new(size_t unSize, int nLine, const char * pFunc)
{
    printf("operator new called, line: %d, func: %s\n",
            nLine, pFunc);
    return malloc(unSize);
}
 
void operator delete(void * pMem)
{
    printf("delete1\n");
    free(pMem);
}
 
class A
{
public:
 
    A(int a = 0) :
        _a(a)
    {   
        printf("constructor called\n");
    }   
    {
        printf("~A()\n");
    }
 
    static void operator delete(void * pMem, size_t unSize)
    {
        printf("delete2: %u\n", unSize);
        free(pMem);
    }
 
private:
 
    int _a;
};
 
class B: public A
{
public:
 
    ~B()
    {
        printf("~B()\n");
    }
 
    int _b;
 
    int _bb;
};
 
int main()
{
    A * pA = new A(10);
    printf("#######\n");
 
    A * pB = new (__LINE__, __func__) B();
    printf("#######\n");
 
    A * szA = new A[10];
    printf("#######\n");
 
    delete pA; 
    printf("#######\n");
 
    delete pB; 
    printf("#######\n");
 
    delete [] szA;
    printf("#######\n");
 
    char * pC = NULL;
    delete pC; 
}
</stdlib></stdio>

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
wuzesheng@wuzesheng-ubuntu:~/Program$ ./a.out 
operator new called
constructor called
#######
operator new called, line: 68, func: main
constructor called
#######
operator new called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
constructor called
#######
~A()
delete2: 8
#######
~B()
~A()
delete2: 16
#######
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
~A()
delete1
#######
delete1

上面的程序很简单,我在这里不做过多的解释,感兴趣的朋友可以自己分析一下。

通过我上面的讲解,相信大多数朋友应该对C++中内存的动态分配与释放有了较为深入的理解。后续我还有可能写一些关于C++中内存管理的文章,只有把本文所讲的内容与后续的内存管理的一些常见的方法结合起来,我们才写出更加健壮的C++程序。欢迎读者朋友留言一起交流!

posted @ 2012-07-16 12:37 Jacc.Kim 阅读(284) | 评论 (0)编辑 收藏

原文出自:http://visionsky.blog.51cto.com/733317/151760

联合(union)在C/C++里面见得并不多,但是在一些对内存要求特别严格的地方,联合又是频繁出现,那么究竟什么是联合、怎么去用、有什么需要注意的地方呢?就这些问题,我试着做一些简单的回答,里面肯定还有不当的地方,欢迎指出!
1、什么是联合?
   “联合”是一种特殊的类,也是一种构造类型的数据结构。在一个“联合”内可以定义多种不同的数据类型, 一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,已达到节省空间的目的(还有一个节省空间的类型:位域)。 这是一个非常特殊的地方,也是联合的特征。另外,同struct一样,联合默认访问权限也是公有的,并且,也具有成员函数。
2、联合与结构的区别?
   “联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。
   下面举一个例了来加对深联合的理解。
    例4:
#include <stdio.h>
void main()
{
        union number
        {                   /*定义一个联合*/
                int i;
                struct
                {             /*在联合中定义一个结构*/
                        char first;
                        char second;
                }half;
        }num;
        num.i=0x4241;         /*联合成员赋值*/
        printf("%c%c\n", num.half.first, num.half.second);
        num.half.first='a';   /*联合中结构成员赋值*/
        num.half.second='b';
        printf("%x\n", num.i);
        getchar();
}
    输出结果为:
     AB
     6261
    从上例结果可以看出: 当给i赋值后, 其低八位也就是first和second的值; 当给first和second赋字符后, 这两个字符的ASCII码也将作为i 的低八位和高八位。
3、如何定义?
   例如:
    union test
    {
      test() { }
      int office;
      char teacher[5];
    };
    定义了一个名为test的联合类型,它含有两个成员,一个为整型,成员名office;另一个为字符数组,数组名为teacher。联合定义之后,即可进行联合变量说明,被说明为test类型的变量,可以存放整型量office或存放字符数组teacher。
4、如何说明?
   联合变量的说明有三种形式:先定义再说明、定义同时说明和直接说明。
   以test类型为例,说明如下:
    1) union test
       {
         int office;
         char teacher[5];
       };
       union test a,b;    /*说明a,b为test类型*/
    2) union test
       {
         int office;
         char teacher[5];
       } a,b;
    3) union
       {
         int office;
         char teacher[5];
       } a,b;
       经说明后的a,b变量均为test类型。a,b变量的长度应等于test的成员中最长的长度,即等于teacher数组的长度,共5个字节。a,b变量如赋予整型值时,只使用了4个字节,而赋予字符数组时,可用5个字节。
5、如何使用?
   对联合变量的赋值,使用都只能是对变量的成员进行。联合变量的成员表示为:     
联合变量名.成员名 
例如,a被说明为test类型的变量之后,可使用a.class、a.office 
不允许只用联合变量名作赋值或其它操作,也不允许对联合变量作初始化赋值,赋值只能在程序中进行。
还要再强调说明的是,一个联合变量,每次只能赋予一个成员值。换句话说,一个联合变量的值就是联合变员的某一个成员值。
6、匿名联合
   匿名联合仅仅通知编译器它的成员变量共同享一个地址,而变量本身是直接引用的,不使用通常的点号运算符语法.例如:
     #i nclude <iostream>
     void main()
     {
         union{
                int test;
                char c;
               };         
        test=5;
        c=′a′;
        std::cout<<i<<" "<<c;
     }
    正如所见到的,联合成分象声明的普通局部变量那样被引用,事实上对于程序而言,这也正是使用这些变量的方式.另外,尽管被定义在一个联合声明中,他们与同一个程序快那的任何其他局部变量具有相同的作用域级别.这意味这匿名联合内的成员的名称不能与同一个作用域内的其他一直标志符冲突.
    对匿名联合还存在如下限制:
    因为匿名联合不使用点运算符,所以包含在匿名联合内的元素必须是数据,不允许有成员函数,也不能包含私有或受保护的成员。还有,全局匿名联合必须是静态(static)的,否则就必须放在匿名名字空间中。
7、几点需要讨论的地方:
   1、联合里面那些东西不能存放?
      我们知道,联合里面的东西共享内存,所以静态、引用都不能用,因为他们不可能共享内存。
   2、类可以放入联合吗?
      我们先看一个例子:
      class Test
      {
      public:
    Test():data(0) { }
      private:
          int data;
      };
     typedef union _test
     {
Test test;  
     }UI;  
     编译通不过,为什么呢?
     因为联合里不允许存放带有构造函数、析够函数、复制拷贝操作符等的类,因为他们共享内存,编译器无法保证这些对象不被破坏,也无法保证离开时调用析够函数。
    3、又是匿名惹的祸??
       我们先看下一段代码:
class test
{
        public:
             test(const char* p);
             test(int in);
             const operator char*() const {return
data.ch;}
             operator long() const {return data.l;}
        private:
     enum type {Int, String };
            union
     {
const char* ch;
int i;
      }datatype;
      type stype;
      test(test&);
      test& operator=(const test&);
        };
       test::test(const char *p):stype
(String),datatype.ch(p)     { }
       test::test(int in):stype(Int),datatype.l(i)     {
}
     看出什么问题了吗?呵呵,编译通不过。为什么呢?难道datatype.ch(p)和datatype.l(i)有问题吗?
     哈哈,问题在哪呢?让我们来看看构造test对象时发生了什么,当创建test对象时,自然要调用其相应的构造函数,在构造函数中当然要调用其成员的构造函数,所以其要去调用datatype成员的构造函数,但是他没有构造函数可调用,所以出
错。
     注意了,这里可并不是匿名联合!因为它后面紧跟了个data!
    4、如何有效的防止访问出错?
       使用联合可以节省内存空间,但是也有一定的风险:通过一个不适当的数据成员获取当前对象的值!例如上面的ch、i交错访问。
       为了防止这样的错误,我们必须定义一个额外的对象,来跟踪当前被存储在联合中的值得类型,我们称这个额外的对象为:union的判别式。
       一个比较好的经验是,在处理作为类成员的union对象时,为所有union数据类型提供一组访问函数。
posted @ 2012-06-27 10:35 Jacc.Kim 阅读(209) | 评论 (0)编辑 收藏

原文出自:http://www.cnblogs.com/JCSU/articles/1051826.html

程序员们经常编写内存管理程序,往往提心吊胆。如果不想触雷,唯一的解决办法就是发现所有潜伏的地雷并且排除它们,躲是躲不了的。本文的内容比一般教科书的要深入得多,读者需细心阅读,做到真正地通晓内存管理。

     内存分配方式 

   (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
   (2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
   (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
 
     常见的内存错误及其对策 

     发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。 常见的内存错误及其对策如下: 

     * 内存分配未成功,却使用了它

编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。

* 内存分配虽然成功,但是尚未初始化就引用它

犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

* 内存分配成功并且已经初始化,但操作越过了内存的边界

例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。

* 忘记了释放内存,造成内存泄露

含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。

动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。

* 释放了内存却继续使用它

有三种情况:

(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。

(2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

(3)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

【规则1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

【规则2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

【规则3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

【规则4】动态内存的申请与释放必须配对,防止内存泄漏。

【规则5】用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

指针与数组的对比

C++/C程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。

指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。

下面以字符串为例比较指针与数组的特性

1 修改内容

下例中,字符数组a的容量是6个字符,其内容为hello。a的内容可以改变,如a[0]= 'x'。指针p指向常量字符串"world"(位于静态存储区,内容为world),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句 p[0]= 'x'有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。

#include<iostream.h>

void main()
{
    
char a[] = "hello";
    a[
0= 'x';
    cout 
<< a << endl;
    
char *= "world"// 注意p指向常量字符串
    p[0= 'x'// 编译器不能发现该错误
    cout << p << endl;
}

2 内容复制与比较

不能对数组名进行直接复制与比较。下例中,若想把数组a的内容复制给数组b,不能用语句 b = a ,否则将产生编译错误。应该用标准库函数strcpy进行复制。同理,比较b和a的内容是否相同,不能用if(b==a) 来判断,应该用标准库函数strcmp进行比较。

语句p = a 并不能把a的内容复制指针p,而是把a的地址赋给了p。要想复制a的内容,可以先用库函数malloc为p申请一块容量为strlen(a)+1个字符的内存,再用strcpy进行字符串复制。同理,语句if(p==a) 比较的不是内容而是地址,应该用库函数strcmp来比较。

// 数组…
char a[] = "hello";
char b[10];
strcpy(b, a); 
// 不能用 b = a;
if(strcmp(b, a) == 0// 不能用 if (b == a)

// 指针…
int len = strlen(a);
char *= (char *)malloc(sizeof(char)*(len+1));
strcpy(p,a); 
// 不要用 p = a;
if(strcmp(p, a) == 0// 不要用 if (p == a)

 3 计算内存容量

用运算符sizeof可以计算出数组的容量(字节数)。下例(a)中,sizeof(a)的值是12(注意别忘了' ')。指针p指向a,但是 sizeof(p)的值却是4。这是因为sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p所指的内存容量。 C++/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。下例(b)中,不论数组a的容量是多少,sizeof(a)始终等于sizeof(char *)。

示例(a)
char a[] = "hello world";
char *= a;
cout
<< sizeof(a) << endl; // 12字节
cout<< sizeof(p) << endl; // 4字节

示例(b)
void Func(char a[100])
{
 cout
<< sizeof(a) << endl; // 4字节而不是100字节
}


 

posted @ 2012-06-25 10:25 Jacc.Kim 阅读(252) | 评论 (0)编辑 收藏

3D游戏开发中,该变换时常发生。下面记录下,以备随时查询用。

说明:
1)
已经向量v就不用说,可以是3维空间中的任意向量。(不论位置在哪都没关系,因为向量是与位置无关的)
2)
绕已知向量n旋转。此处的n向量,最好要规范化成单位向量。为什么需要此要求呢?其实很简单,如果不规范化成单位向量,那么变换后的结果向量(假如称为v'),其模就不会等于原向量v的模。因此就会出错。(当然,如果非要用非规范化的单位向量参与计算的话,我们也是可以通过将v'进行一些处理,而得到最终想要的向量。)

下面,确认一下,变换矩阵以及变化公式所需的参数信息:
v(vx     ,      vy     ,  vz)                                :
为已经向量
n(nx    ,    ny     ,  nz)                                :
v向量绕n旋转的基向量
R(n     ,    angle)                                         : 为旋转所需的变换矩阵。(注释:angle就是角度,本来可以直接用那些希腊字母,结果发现用了,在此,我就输入不了中文与英文字符了。蛋疼)

v'(vx'   ,    vy'     , vz')                                : 为变换后最终要得到的结果向量

根据3D变换基础知识,可得如下公式:
v' = vR(n, angle)
其中,v向量已知。R(n, angle)根据推导,可得如下式子:
                     [ p' ]    [ nx2(1 - cos(angle)) + cos(angle)                            nxny(1 - cos(angle)) + nzsin(angle)                       nxnz(1 - cos(angle)) - nysin(angle) ]
R(n, angle) =  | q' | = | nxny(1 - cos(angle)) - nzsin(angle)                        ny2(1 - cos(angle)) + cos(angle)                           nynz(1 - cos(angle)) + nxsin(angle) |
                     [ r' ]     [ nxnz(1 - cos(angle)) + nysin(angle)                       nynz(1 - cos(angle)) - nxsin(angle)                        nz2(1 - cos(angle)) + cos(angle)    ]

所以,可得最终的v'向量。具体如下:

v' = vR = (vx, vy, vz)R;

vx'   = vx * (nx2(1 - cos(angle)) + cos(angle));
vx' += vy * (nxny(1 - cos(angle)) - nzsin(angle));
vx' += vz * (nxnz(1 - cos(angle)) + nysin(angle));

vy'   = vx * (nxny(1 - cos(angle)) + nzsin(angle));
vy' += vy * (ny2(1 - cos(angle)) + cos(angle));
vy' += vz * (nynz(1 - cos(angle)) - nxsin(angle));

vz'   = vx * (nxnz(1 - cos(angle)) - nysin(angle));
vz' += vy * (nynz(1 - cos(angle)) + nxsin(angle));
vz' += vz * (nz2(1 - cos(angle)) + cos(angle));

-----------------------------------------------------------------
有了上面的知识,现在就可以很容易理解。如下一段旋转摄像机方向的代码了:(注:代码出自徐明亮作者所著《opengl游戏编程》一书中的内容)

/**  旋转摄像机方向  */
void Camera::rotateView(float angle, float x, float y, float z)
{
    Vector3 newView;

    
/** 计算方向向量 */
    Vector3 view 
= m_View - m_Position;        

    
/** 计算 sin 和cos值 */
    
float cosTheta = (float)cos(angle);
    
float sinTheta = (float)sin(angle);

    
/** 计算旋转向量的x值 */
    newView.x  
= (cosTheta + (1 - cosTheta) * x * x)        * view.x;
    newView.x 
+= ((1 - cosTheta) * x * y - z * sinTheta)    * view.y;
    newView.x 
+= ((1 - cosTheta) * x * z + y * sinTheta)    * view.z;

    
/** 计算旋转向量的y值 */
    newView.y  
= ((1 - cosTheta) * x * y + z * sinTheta)    * view.x;
    newView.y 
+= (cosTheta + (1 - cosTheta) * y * y)        * view.y;
    newView.y 
+= ((1 - cosTheta) * y * z - x * sinTheta)    * view.z;

    
/** 计算旋转向量的z值 */
    newView.z  
= ((1 - cosTheta) * x * z - y * sinTheta)    * view.x;
    newView.z 
+= ((1 - cosTheta) * y * z + x * sinTheta)    * view.y;
    newView.z 
+= (cosTheta + (1 - cosTheta) * z * z)        * view.z;

    
/** 更新摄像机的方向 */
    m_View 
= m_Position + newView;
}



posted @ 2012-06-12 11:29 Jacc.Kim 阅读(1680) | 评论 (0)编辑 收藏

     摘要: 下面这个地址,介绍了几个c++中的关键字。还不错,就收下了。http://www.cppblog.com/leetaolion/archive/2011/09/13/46586.html C++关键字:mutable、volatile、explicit以及__based      mutable关键字     关键字mu...  阅读全文
posted @ 2012-05-29 13:59 Jacc.Kim 阅读(228) | 评论 (0)编辑 收藏

好久没有写博客了。这里记录一点关于ccLayer的触摸处理的记录,怕将来忘记。
要使用ccLayer,必要要处理它的触摸响应。否则就少了必要的交互。
关于ccLayer的触摸有两种类型。
1) 单点触摸
2) 多点触摸
下面分别总结一下:
1) 单点触摸
要使用单点触摸,必须要重写以下几个接口:
 virtual void onEnter();//必须
 virtual void onExit();//必须
 virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);//必须
 virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);//可选,但一般情况下要
 virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);//可选,但一般情况下要
 virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);//可选
a) onEnter();接口。在此接口中,需要调用父类的该接口,并且最重要的,要为当前对象注册一个触摸委托(即:代理)。参考如下代码:
 CCLayer::onEnter();
 CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
b) onExit();接口。在此接口中,需要将在onEnter()中注册的当前对象的触摸委托给移除掉。然后调用父类的onExit();。参考代码如下:
 CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
 CCLayer::onExit();
c) ccTouchBegan();该接口,细心的人可能会发现,就它是返回bool的。它的返回值决定着,后续的ccTouchMoved();ccTouchEnded();ccTouchCancelled();是否触发。只有返回true时才触发。
关于ccTouchBegan();ccTouchMoved();ccTouchEnded();ccTouchCancelled();的作用,在此我就不多说了。看名字我想就应该能清楚。

2) 多点触摸
要使用多点触摸。则只需要实现重写如下几个接口,即可:
 virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
 virtual void ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
 virtual void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);
它们的作用我也不多说。下面简要介绍下CCSet*对象。它存储着所有的触摸信息。即:遍历它,可以处理所有的触摸点响应。其他的用法同单点触摸一样。
注意:用多点触摸时,不需要注册上面第 1) 点中的a)与b)小点的信息。(如果我没记错的话,处理了,可能会让程序蹦掉。)
posted @ 2012-04-11 12:08 Jacc.Kim 阅读(5650) | 评论 (0)编辑 收藏

     摘要: 原文转自:http://www.cppblog.com/tx7do/archive/2006/04/24/6146.html TinyXml学习笔记张弛 <zhangchi@china.com>一、      TinyXml 的特点TinyXml 是一个基于 DOM ...  阅读全文
posted @ 2012-02-14 17:40 Jacc.Kim 阅读(346) | 评论 (0)编辑 收藏

CCDirector::sharedDirector()->getWinSize();
CCSprite::setPosition(...);
CCSprite::getPosition();

以上这些都是以 点 为单位的(不是像素)。
猜测:所有在cocos2d中有关x与y有关的,应该都是以点为单位。
posted @ 2012-02-01 09:48 Jacc.Kim 阅读(340) | 评论 (0)编辑 收藏

1) 场景创建出来后,如果有autorelease。则在该场景被替换掉时,先前场景将被释放。(这点在cc的管理器中有注释说明)
2) 场景创建出来后,如果没有autorelease。则在被添加进管理器后,请一定要release一次。否则将会有内存泄漏
posted @ 2012-01-18 10:31 Jacc.Kim 阅读(411) | 评论 (0)编辑 收藏

1) CCMenuItem*对象在创建出来后并添加到CCMenu*对象后,只要CCMenu对象有释放,CCMenuItem*对象也将被释放。即:CCMenuItem*对象不需要再将释放。。否则可能会蹦。
2) CCMenu*对象被addChild到父对象后,最后只要remove出来就可以。remove出来后,CCMenu*对象就被释放掉。(原因:因为CCMenu*对象被创建出来时,它是autorelease()的)

 ccColor3B color;
 color.r = 0;
 color.g = 0;
 color.b = 255;
 m_pPlayMenuItem = CCMenuItemFont::itemFromString("Play", this, menu_selector(CWelcomeScene::OnPlayGameMenuItemClicked));
 ((CCMenuItemFont*)m_pPlayMenuItem)->setFontSizeObj(12);
 ((CCMenuItemFont*)m_pPlayMenuItem)->setColor(color);
 
 color.r = 255;
 color.g = 0;
 color.b = 0;
 m_pExitMenuItem = CCMenuItemFont::itemFromString("Exit", this, menu_selector(CWelcomeScene::OnExitMenuItemClicked));
 ((CCMenuItemFont*)m_pExitMenuItem)->setFontSizeObj(12);
 ((CCMenuItemFont*)m_pExitMenuItem)->setColor(color);

 if (NULL == m_pMenu)
 {
  m_pMenu = CCMenu::menuWithItems(m_pPlayMenuItem, m_pExitMenuItem, NULL);
  this->addChild(m_pMenu, 1);
  m_pMenu->alignItemsVerticallyWithPadding(0.0f);
 }


 m_pPlayMenuItem = CCMenuItemImage::itemFromNormalImage(g_pcszStartNormalMI, g_pcszStartSelectedMI, this, menu_selector(CWelcomeScene::OnPlayGameMenuItemClicked));
 m_pExitMenuItem = CCMenuItemImage::itemFromNormalImage(g_pcszCloseNormalMI, g_pcszCloseSelectedMI, this, menu_selector(CWelcomeScene::OnExitMenuItemClicked)); 
 m_pPlayMenuItem->setScale(0.4f);

 m_pMenu = CCMenu::menuWithItems(m_pPlayMenuItem, m_pExitMenuItem, NULL);
 this->addChild(m_pMenu, 1);

 float fX = GetCurrentWinSize(true).width / 2.0f - 10.0f;
 float fY = -(GetCurrentWinSize(false).height / 2.0f) + 5.0f;
 m_pExitMenuItem->setAnchorPoint(ccp(1.0, 0.0f));
 m_pExitMenuItem->setPosition(ccp(fX, fY));

 fX -= m_pExitMenuItem->getContentSize().width + 2.0f;
 fY -= 2.0f;
 m_pPlayMenuItem->setAnchorPoint(ccp(1.0f, 0.0f));
 m_pPlayMenuItem->setPosition(ccp(fX, fY));



posted @ 2012-01-18 10:25 Jacc.Kim 阅读(763) | 评论 (1)编辑 收藏

仅列出标题
共14页: First 5 6 7 8 9 10 11 12 13 Last