置顶随笔

[置顶]浅谈C++类(4)--隐式类类型转换

     摘要: 欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。老规矩,看个例子,知道我要说的是什么。 例4.0: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1#include <st...  阅读全文

posted @ 2011-07-05 20:32 Smart Pointer 阅读(223) | 评论 (0)编辑 收藏

2011年11月1日

杂记

关于C++函数声明的问题 :加上{}后就不能算是声明了,而是已经定义了一个函数!

posted @ 2011-11-01 18:22 Smart Pointer 阅读(148) | 评论 (0)编辑 收藏

2011年8月6日

C++引用与指针的比较

引用是C++中的概念,初学者容易把引用和指针混淆一起。 
一下程序中,n是m的一个引用(reference),m是被引用物(referent)。 
int m; 
int &n = m; 
n相当于m的别名(绰号),对n的任何操作就是对m的操作。 
所以n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。 

引用的规则: 

(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。 
(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。 
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。 

以下示例程序中,k被初始化为i的引用。 
语句k = j并不能将k修改成为j的引用,只是把k的值改变成为6。 
由于k是i的引用,所以i的值也变成了6。 
int i = 5; 
int j = 6; 
int &k = i; 
k = j; // k和i的值都变成了6; 

引用的主要功能是传递函数的参数和返回值。 

C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。 

以下是"值传递"的示例程序。 

由于Func1函数体内的x是外部变量n的一份拷贝,改变x的值不会影响n, 所以n的值仍然是0。 
void Func1(int x) 

x = x + 10; 

... 
int n = 0; 
Func1(n); 
cout << "n = " << n << endl; // n = 0 

以下是"指针传递"的示例程序。 

由于Func2函数体内的x是指向外部变量n的指针,改变该指针的内容将导致n的值改变,所以n的值成为10。 
void Func2(int *x) 

(* x) = (* x) + 10; 

... 
int n = 0; 
Func2(&n); 
cout << "n = " << n << endl; // n = 10 

以下是"引用传递"的示例程序。 

由于Func3函数体内的x是外部变量n的引用,x和n是同一个东西,改变x等于改变n,所以n的值成为10。 
void Func3(int &x) 

x = x + 10; 

... 
int n = 0; 
Func3(n); 
cout << "n = " << n << endl; // n = 10 

对比上述三个示例程序,会发现"引用传递"的性质象"指针传递",而书写方式象"值传递"。 

实际上"引用"可以做的任何事情"指针"也都能够做,为什么还要"引用"这东西? 
答案是"用适当的工具做恰如其分的工作"。 

指针能够毫无约束地操作内存中的任何东西,尽管指针功能强大,但是非常危险。 

如果的确只需要借用一下某个对象的"别名",那么就用"引用",而不要用"指针",以免发生意外。

posted @ 2011-08-06 19:34 Smart Pointer 阅读(250) | 评论 (0)编辑 收藏

2011年8月5日

OSG节点访问和遍历

节点访问:

  OSG中节点的访问使用的是一种访问器模式。

  一个典型的访问器涉及抽象访问者角色(Visitor), 具体访问者(Concrete Visitor), 节点角色(Node)。

  OSG中访问者角色为NodeVisitor类,其基本结构如下:

  NodeVisitor(TraversalMode tm)    //构造函数,TraversalMode为节点树的遍历方式

  //TRAVERSE_NONE, 仅当前节点

  //TRAVERSE_PARENTS, 向当前节点的父节点遍历

  //TRAVERSE_ALL_CHILDREN, 向子节点遍历

  void traverse(Node& node)  //向下一个需要访问的节点推进

  void apply(Node& node)   //虚函数,访问各种节点类型,并执行访问器中的自定义操作

  void apply(Group& node)

  void apply(Geode& node)

  …………

  NodeVisitor 只是访问器角色的抽象接口,要使用访问器访问节点并执行自定义操作时,需要继承并重写

  apply(……)函数实现自定义功能。osg::Node类中的访问接口为 void accept(NodeVisitor& nv)。对节点

  的访问从节点接受一个访问器开始,将一个具体的访问器对象传递给节点,节点反过来执行访问器的apply(...)

  函数,并将自己传入访问器。可如下简单表示:

  void Node::accept(NodeVisitor& nv)

  {

  nv.apply(*ths) ;

  }

  遍历节点树:

  osg::Node类中有两个辅助函数:

  void ascend(NodeVisitor& nv)     //虚函数,向上一级节点推进访问器

  void traverse(NodeVisitor& nv)   //虚函数,向下一级节点推进访问器

  NodeVisitor的traverse()函数实现如下:

  inline void traverse(Node& node)

  {

  if (_traversalMode == TRAVERSE_PARENTS)

  node.ascend(*this);

  else if (_traversalMode != TRAVERSE_NONE)

  node.traverse(*this);

  }

  示例如下:

  代码

  1 #include <osg/Node>

  2 #include <osgDB/ReadFile>

  3 #include <iostream>

  4

  5  using namespace std;

  6

  7 class InfoVisitor: public osg::NodeVisitor

  8 {

  9 public:

  10     InfoVisitor()

  11         :osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), _indent(0)

  12     {}

  13

  14     virtual void apply(osg::Node& node)

  15     {

  16         for(int i = 0; i < _indent; i++)  cout << "    ";

  17         cout << "[" << _indent << "]"<< node.libraryName()

  18                << "::" << node.className() << endl;

  19

  20         _indent++;

  21         traverse(node);

  22         _indent--;

  23

  24        for(int i = 0; i < _indent; i++)  cout << "    ";

  25         cout << "[" << _indent << "] "<< node.libraryName()

  26                << "::" << node.className() << endl;

  27     }

  28

  29     virtual void apply(osg::Geode& node)

  30     {

  31         for(int i = 0; i < _indent; i++)  cout << "    ";

  32         cout << "[" << _indent << "] "<< node.libraryName()

  33                 << "::" << node.className() << endl;

  34

  35         _indent++;

  36

  37         for(unsigned int n = 0; n < node.getNumDrawables(); n++)

  38         {

  39             osg::Drawable* draw = node.getDrawable(n);

  40             if(!draw)

  41                 continue;

  42             for(int i = 0; i <  _indent; i++)  cout << "    ";

  43             cout << "[" << _indent << "]" << draw->libraryName() << "::"

  44                    << draw->className() << endl;

  45         }

  46

  47         traverse(node);

  48         _indent--;

  49

  50         for(int i = 0; i < _indent; i++)  cout << "    ";

  51         cout << "[" << _indent << "]"<< node.libraryName()

  52                 << "::" << node.className() << endl;

  53     }

  54 private:

  55     int _indent;

  56 };

  57

  58 int main(int argc, char** argv)

  59 {

  60     osg::ArgumentParser  parser(&argc, argv);

  61     osg::Node* root = osgDB::readNodeFiles(parser);

  62

  63     if(!root)

  64     {

  65         root = osgDB::readNodeFile("avatar.osg");

  66     }

  67

  68     InfoVisitor infoVisitor;

  69     if(root)

  70     {

  71         root->accept(infoVisitor);

  72     }

  73

  74     system("pause");

  75     return 0;

  76 }

posted @ 2011-08-05 10:39 Smart Pointer 阅读(323) | 评论 (0)编辑 收藏

2011年7月19日

OSG开源教程(转)

     摘要: 整理:荣明、王伟北 京2008年4月序第一次接触OSG是在2001年,当时开源社区刚刚兴起,还没有现在这么火。下载了OSG源码,但是在看了几个Demo之后,感觉没有什么特别之处。时隔七年之后,我再次将目光投向OSG,发现OSG确实有其独到之处,很多3D效果已经不弱于甚至超过商业软件,有感于开源力量的巨大。但是,与当前主流3D商业软件如Vega、VegaPrime、VTree、Performer等相...  阅读全文

posted @ 2011-07-19 21:06 Smart Pointer 阅读(4394) | 评论 (0)编辑 收藏

OSG开发概览

     摘要: 1 OSG基础知识Ø OSG是Open Scene Graphic 的缩写,OSG于1997年诞生于以为滑翔机爱好者之手,Don burns  为了对滑翔机的飞行进行模拟,对openGL的库进行了封装,osg的雏形就这样诞生了,1998年Don burns 遇到了同样喜欢滑翔机和计算机图形...  阅读全文

posted @ 2011-07-19 21:01 Smart Pointer 阅读(4054) | 评论 (0)编辑 收藏

2011年7月8日

C++类继承关系问题

                             C++类继承关系问题
    在C++中继承主要有三种关系:public、protected和private。这三种继承关系中public
继承是最为常用的一种继承关系,代表了接口继承含义,而他们分别具体代表了什么含义呢?
1. public
   从语义角度上来说,public继承是一种接口继承,根据面向对象中的关系而言就是,子类
   可以代替父类完成父类接口所声明的行为,也就是必须符合“Liskov替换原则(LSP)”,
   此时子类可以自动转换成为父类的接口,完成接口转换。
   从语法角度上来说,public继承会保留父类中成员(包括函数和变量等)的可见性不变,
   也就是说,如果父类中的某个函数是public的,那么在被子类继承后仍然是public的。
   
2. protected
   从语义角度上来说,protected继承是一种实现继承,根据面向对象中的关系而言就是,
   子类不能代替父类完成父类接口所声明的行为,也就是不符合“Liskov替换原则(LSP)”,
   此时子类不能自动转换成为父类的接口,就算通过类型转换(static_cast和dynamic_cast)
   也会得到一个空指针。
   从语法角度上来说,protected继承会将父类中的public可见性的成员修改成为protected
   可见性,相当于在子类中引入了protected成员,这样一来在子类中同样还是可以调用父
   类的protected和public成员,子类的子类就也可以调用被protected继承的父类的protected
   和public成员。
   例如:
       class CSample1 {
       protected:
           void printProtected() {}
       public:
           void printPublic() {}
       };
       class CSample2 : protected CSample1 {
       
       };
       class CSample3 : public CSample2 {
           void print3() {
               printProtected();
               printPublic();
           }
       };
3. private
   从语义角度上来说,private继承是一种实现继承,根据面向对象中的关系而言就是,
   子类不能代替父类完成父类接口所声明的行为,也就是不符合“Liskov替换原则(LSP)”,
   此时子类不能自动转换成为父类的接口,就算通过类型转换(static_cast和dynamic_cast)
   也会得到一个空指针。
   从语法角度上来说,private继承会将父类中的public和protected可见性的成员修改成为
   private可见性,这样一来虽然子类中同样还是可以调用父类的protected和public成员,
   但是在子类的子类就不可以再调用被private继承的父类的成员了。
       class CSample1 {
       protected:
           void printProtected() {}
       public:
           void printPublic() {}
       };
       class CSample2 : private CSample1 {
       
       };
       class CSample3 : public CSample2 {
           void print3() {
               printProtected(); // 编译错误,不可以调用该函数
               printPublic();    // 编译错误,不可以调用该函数
           }
       };
 
    在面向对象的理论中有两种概念:接口、实现,所以就出现了所谓的接口继承和实现继
承两种关系。而protected和private就是实现继承中所要用到的,其实protected和private
两者则约束继承时并没有形成两种不同的继承类别,而仅仅只是为了方便C++类方法的传递
调用而设计的,其实在java这样面向对象要求更为严格的语言当中,没有实现继承,他必须
通过委托方式来完成这一概念,如果熟悉java就会明白,如果一个对象要使用另外一个对象
的接口功能,而自身又不能够充当该对象所扮演的角色时,就会通过委托来完成,这样一来
就必须在对象中包含一个委托对象,通过对象调用语法来完成功能;在C++中就可以通过
protected和private继承来完成java中的委托关系(当然C++也可以形成对象委托关系),
那么这种情况下protected继承就容许委托可以传递(也就是被多级子类调用),而private
继承是不容许委托被传递的。

posted @ 2011-07-08 18:37 Smart Pointer 阅读(232) | 评论 (0)编辑 收藏

经典c程序100例(31--40)

     摘要: 【程序31】题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续   判断第二个字母。1.程序分析:用情况语句比较好,如果第一个字母一样,则判断用情况语句或if语句判断第二个字母。2.程序源代码:【程序31】题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续   判断第二个字母。1.程序分析:用情况语句比较好,如果第一个字母一样,则判断用情况语句或...  阅读全文

posted @ 2011-07-08 13:09 Smart Pointer 阅读(147) | 评论 (0)编辑 收藏

2011年7月7日

C++类模板 (非类型模板参数)

非类型模板参数

对于函数模板和类模板,模板参数并不局限于类型,普通值也可以作为模板参数。在基于类型参数的模板中,你定义了一些具体细节未加确定的代码,直到代 码被调用时这些细节才被真正确定。然而,在这里,我们面对的这些细节是值(value),而不是类型。当要使用基于值的模板时,你必须显式地指定这些值, 才能够对模板进行实例化,并获得最终代码。在这一章里,我们将使用一个新版本的stack类模板来叙述这个特性。另外,我们还给出了一个非类型函数模板参 数的例子,并且讨论了这一技术的某些限制。

4.1  非类型的类模板参数

较之前一章stack例子的实现,你也可以使用元素数目固定的数组来实现stack。这个方法(用固定大小的数组)的优点是:无论是由你来亲自管理 内存,还是由标准容器来管理内存,都可以避免内存管理开销。然而,决定一个栈(stack)的最佳容量是很困难的。如果你指定的容量太小,那么栈可能会溢 出;如果指定的容量太大,那么可能会不必要地浪费内存。一个好的解决方法就是:让栈的用户亲自指定数组的大小,并把它作为所需要的栈元素的最大个数。

为了做到这一点,你需要把数组大小定义为一个模板参数:

//basics/stack4.hpp
#include <stdexcept>

//template<typename T = int, int MAXSIZE = 100>   //可以为模板参数指定缺省值

template <typename T, int MAXSIZE>             //由用户自己设定
class Stack {
private:
T elems[MAXSIZE];        // 包含元素的数组
int numElems;            // 元素的当前总个数

  public:
Stack();                  // 构造函数
void push(T const&);    // 压入元素
void pop();               // 弹出元素
T top() const;           // 返回栈顶元素
bool empty() const {    // 返回栈是否为空
return numElems == 0;
}
bool full() const {     // 返回栈是否已满
return numElems == MAXSIZE;
}
};

// 构造函数
template <typename T, int MAXSIZE>
Stack<T,MAXSIZE>::Stack ()
: numElems(0)               // 初始时栈不含元素
{
// 不做任何事情
}

template <typename T, int MAXSIZE>
void Stack<T,MAXSIZE>::push (T const& elem)
{
if (numElems == MAXSIZE) {
throw std::out_of_range("Stack<>::push(): stack is full");
}
elems[numElems] = elem;   // 附加元素
++numElems;               // 增加元素的个数
}

template<typename T, int MAXSIZE>
void Stack<T,MAXSIZE>::pop ()
{
if (numElems <= 0) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
--numElems;               // 减少元素的个数
}

template <typename T, int MAXSIZE>
T Stack<T,MAXSIZE>::top () const
{
if (numElems <= 0) {
throw std::out_of_range("Stack<>::top(): empty stack");
}
return elems[numElems-1];  // 返回最后一个元素
}

MAXSIZE是新加入的第2个模板参数,类型为int;它指定了数组最多可包含的栈元素的个数:
 template<typename T, int MAXSIZE>
class Stack {
private:
T elems[MAXSIZE]; //包含元素的数组
...
};

另外,我们使用push()来检查该栈是否已经满了:
template <typename T, int MAXSIZE>
void Stack<T, MAXSIZE>::push (T const& elem)
{
if (numElems = = MAXSIZE ){
throw std::out_of_range ("Stack<>::push():stack is full")
}
elems [numElems] = elem;  //附加元素
++numElems;    //增加元素的个数
}

为了使用这个类模板,你需要同时指定元素的类型和个数(即栈的最大容量):
//basics/stack4test.cpp
#include <iostream>
#include <string>
#include <cstdlib>
#include "stack4.hpp"

int main()
{
try {
Stack<int,20>  int20Stack;  // 可以存储20个int元素的栈
Stack<int,40>  int40Stack;  // 可以存储40个int元素的栈
Stack<std::string,40> stringStack; // 可存储40个string元素的栈

        // 使用可存储20个int元素的栈
int20Stack.push(7);
std::cout << int20Stack.top() << std::endl;
int20Stack.pop();

        // 使用可存储40个string的栈
stringStack.push("hello");
std::cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (std::exception const& ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
return EXIT_FAILURE;  // 退出程序且有ERROR标记
}
}

可以看出,每个模板实例都具有自己的类型,因此int20Stack和int40Stack属于不同的类型,而且这两种类型之间也不存在显式或者隐式的类型转换;所以它们之间不能互相替换,更不能互相赋值。

同样,我们可以为模板参数指定缺省值:

template<typename T = int, int MAXSIZE = 100>
class Stack {
...
};

然而,如果从优化设计的观点来看,这个例子并不适合使用缺省值。缺省值应该是直观上正确的值。但是对于栈的类型和大小而言,int类型和最大容量 100从直观上看起来都不是正确的。因此,在这里最好还是让程序员显式地指定这两个值。因此我们可以在设计文档中用一条声明来说明这两个属性(即类型和最 大容量)。

转自:http://book.51cto.com/art/200803/68250.htm

posted @ 2011-07-07 17:26 Smart Pointer 阅读(1811) | 评论 (0)编辑 收藏

C++里try,catch,throw的用法

#include<iostream>
#include 
<string>
using namespace std;
class Person
{
private:
 
int age;
 
string name;
public:
 
void setAge(int);
 
void setName(string);
};
class Error
{
public:
    
virtual void show()=0;
};
class nameError:public Error
{
public:
 
void show()
 {
   cout
<<"name is error"<<endl;
 }
 
};
class ageError:public Error
{
public:
 
void show()
 {
   cout
<<"age is error"<<endl;
 }
};
void Person::setAge(int a)
{
 ageError ag;
 
if(a<0||a>100)
  
throw ag;
 
this->age=a;
}
void Person::setName(string str)
{
 nameError ne;
 
if(str=="exit")
  
throw ne;
 
this->name=str;
}

int main(void)
{
  Person p;
 
try
 {
  p.setAge(
0);
  p.setName(
"exit");
 }
 
catch(Error &er)
 {
   er.show();
 }
 cout
<<"hello world"<<endl;
 
return 0;
}

posted @ 2011-07-07 16:46 Smart Pointer 阅读(1642) | 评论 (1)编辑 收藏

C++模板使用介绍

1. 模板的概念。

我们已经学过重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同。正确的调用重载函数。例如,为求两个数的最大值,我们定义MAX()函数需要对不同的数据类型分别定义不同重载(Overload)版本。

//函数1.

int max(int x,int y);
{return(x>y)?x:y ;}

//函数2.
float max( float x,float y){
return (x>y)? x:y ;}

//函数3.
double max(double x,double y)
{return (c>y)? x:y ;}

但如果在主函数中,我们分别定义了 char a,b; 那么在执行max(a,b);时 程序就会出错,因为我们没有定义char类型的重载版本。

现在,我们再重新审视上述的max()函数,它们都具有同样的功能,即求两个数的最大值,能否只写一套代码解决这个问题呢?这样就会避免因重载函数定义不 全面而带来的调用错误。为解决上述问题C++引入模板机制,模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。

2.   函数模板的写法

函数模板的一般形式如下:

Template <class或者也可以用typename T>

返回类型 函数名(形参表)
{//
函数定义体 }

说明: template是一个声明模板的关键字,表示声明一个模板关键字class不能省略,如果类型形参多余一个 ,每个形参前都要加class <类型 形参表>可以包含基本数据类型可以包含类类型.

请看以下程序:

//Test.cpp

#include <iostream>

using std::cout;

using std::endl;

//声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替,

//T可以被任何字母或者数字代替。

template <class T>

T min(T x,T y)

return(x<y)?x:y;}

void main( )

{

     int n1=2,n2=10;

     double d1=1.5,d2=5.6;

     cout<< "较小整数:"<<min(n1,n2)<<endl;

     cout<< "较小实数:"<<min(d1,d2)<<endl;

     system("PAUSE");

}

程序运行结果: 

 

程序分析:main()函数中定义了两个整型变量n1 , n2 两个双精度类型变量d1 , d2然后调用min( n1, n2); 即实例化函数模板T min(T x, T y)其中T为int型,求出n1,n2中的最小值.同理调用min(d1,d2)时,求出d1,d2中的最小值.

3. 类模板的写法

定义一个类模板:

Template < class或者也可以用typename T >
class
类名{
//类定义......
};

说明:其中,template是声明各模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个。

例如:定义一个类模板:

// ClassTemplate.h
#ifndef ClassTemplate_HH

#define ClassTemplate_HH

template<typename T1,typename T2>

class myClass{

private:

     T1 I;

     T2 J;

public:

     myClass(T1 a, T2 b);//Constructor

     void show();

};

//这是构造函数

//注意这些格式

template <typename T1,typename T2>

myClass<T1,T2>::myClass(T1 a,T2 b):I(a),J(b){}

//这是void show();

template <typename T1,typename T2>

void myClass<T1,T2>::show()

{

     cout<<"I="<<I<<", J="<<J<<endl;

}

#endif

// Test.cpp

#include <iostream>

#include "ClassTemplate.h"

using std::cout;

using std::endl;

void main()

{

     myClass<int,int> class1(3,5);

     class1.show();

     myClass<int,char> class2(3,'a');

     class2.show();

     myClass<double,int> class3(2.9,10);

     class3.show();

     system("PAUSE");

}

最后结果显示:

 

4.非类型模版参数

一般来说,非类型模板参数可以是常整数(包括枚举)或者指向外部链接对象的指针。

那么就是说,浮点数是不行的,指向内部链接对象的指针是不行的。


template<typename T, int MAXSIZE>

class Stack{

Private:

       T elems[MAXSIZE];

};

Int main()

{

       Stack<int, 20> int20Stack;

       Stack<int, 40> int40Stack;

};

 

 

posted @ 2011-07-07 16:12 Smart Pointer 阅读(200) | 评论 (0)编辑 收藏

仅列出标题  下一页
<2019年2月>
272829303112
3456789
10111213141516
17181920212223
242526272812
3456789

导航

统计

常用链接

留言簿

随笔档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜