当前位置:  编程技术>c/c++/嵌入式

C++你可能不知道地方小结

    来源: 互联网  发布时间:2014-10-11

    本文导语:  下面详细介绍 一、初始化与初始赋值 首先说说类的初始化与初始赋值之前的区别,这也许里面可能有我们不知道的事情。 其实类初始化与初始赋值还是有区别的。 代码如下: class People{ public: People(std::string name,int age,int height)...

下面详细介绍

一、初始化与初始赋值
首先说说类的初始化与初始赋值之前的区别,这也许里面可能有我们不知道的事情。
其实类初始化与初始赋值还是有区别的。
代码如下:

class People{
public:
People(std::string name,int age,int height);
private:
std::string m_sName;
int m_iAge;
int m_iHeight;
}
//赋值
People::People(std::string name,int age,int height)
{
m_sName=name;
m_iAge=age;
m_iHeight=height;
}
//初始化列表
People::People(std::string name,int age,int height)
:m_sName(name),m_iAge(age),m_iHeight(height)
{}

C++规定,对象的成员变量初始化动作发生在进入构造函数本体之前。在构造函数内成员变量赋值都不是初始化,而是赋值。
赋值时首先调用默认构造函数为m_sName,m_iAge,m_iHeight赋初始值,然后在立刻调用赋值操作符进行赋新值。
成员初始列表是将各个成员变量实参都作为复制构造函数的实参。
所以看出赋值相对于初始化,多了一步就是使用赋值操作符进行赋值。所以初始化的效率比赋值的效率高多了。但是对于内置类型,它们效率是一样的。

二、空类
  想想你如果声明一个空类,C++编译器会对它做什么呢?编译器就会为它声明一个复制构造函数,赋值操作符和一个析构函数,以及默认构造函数。所有这些函数都是public而且inline函数。
编译器写的赋值构造函数和赋值操作符,只是单纯地将来源对象的每个non-static变量拷贝到目标对象,具体是进行位拷贝。
如果声明了一个构造函数,编译器是不会创建默认构造函数。
  如果不希望类支持拷贝构造函数与赋值操作符怎么办?不声明?按照上面说明编译器会自动帮你生成。那么可以将它们声明为private,这样阻止编译器自动生成拷贝构造函数(public)。private成功阻止他人使用,但是这并不安全。因为类成员函数以及友元函数还是可以调用private的拷贝构造函数和赋值操作符。
如果只在private下声明拷贝函数和赋值操作符,在有人通过类成员函数去以及member函数去调用它,会获得一个连接错误。那么这里能不能将错误在编译的时候体现出来呢?这里只用将拷贝函数声明为private,并且不在自身,就可以办到了。显然继承一个拷贝函数和赋值操作符为private的基类就办到了,基类如下:
代码如下:

class NonCopyable{
         protected:
                  NonCopyable (){}
                 ~  NonCopyable (){}
         private:
              NonCopyable (const  NonCopyable &);
              NonCopyable & operater=(const  NonCopyable &);
         };

原因是类成员函数或者友元函数尝试拷贝对象,编译器便会尝试生成一个复制构造函数与赋值操作符,并会调用基类的对应函数,但是会被拒绝,因为基类这些函数是private。

3、++函数

  下面说说“*++"与"++*"中你不知道的事情,c++规定后缀形式自加函数有一个int类型参数,当函数被调用时,便其一传递一个0作为int参数的值传递给该函数,而前缀形式自己函数,类型参数没有要求,所以这样就能区分一个++函数是前缀形式与后缀形式了,具体代码如下:
代码如下:

class UPInt{
public
UPInt& operator++( ) ; //++ 前缀
const UPInt operator++( int ); //++后缀
UPInt& operator --( ); // --前缀
const UPInt operator --( int ) //--后缀
UPInt& operator +=( int ); //
...
};

UPInt & UPInt::operator++( )
{
*this += 1;
return *this;
}

const UPInt UPInt :: operator++( int )
{
UPInt oldValue = *this;
++(*this);
return oldValue;
}

后缀函数使用返回参数类型const,是为了避免下面代码生效
代码如下:

 UPInt i;
 i++++;

这个时候第一次调用++返回cosnt对象,并再次调用然后这个函数是non-const成员函数,所以const对象无法调用这个函数,那么i++++就无法生效了。
这里说说效率问题,我们可以看到后缀++函数建立一个临时对象以作为它返回值,这个临时对象经过构造并在最后被析构。而前缀++函数没有这样的临时变量,并且没有那样的操作。所以如果我们在程序中使用前缀++效率会更加高一些,没有了临时变量的构造与析构的动作。

4.虚析构函数
带有多态性质的base class应该声明一个virtual析构函数。
为什么这么说呢?看下面例子
代码如下:

        class base
        { ... }
        class derived:public base
        {... }

        base * p= new derived;    
 
 假设这里基类的析构函数不是virtual,当使用完p指针,我们删除它的时候,想想会发生什么,因为基类的析构函数是non-virtual所以不会发生多态直接调用基类析构函数,仅仅删除继承类中基类那部分内容,那么继承类对象其他内存没有被销毁,从而资源泄漏。
    如果将其声明为virtual,那么就会发生多态,调用的是指向继承类的指针,那么就会销毁的是整个继承类象。

5.传递方式用引用
 缺省情况下c++以值传递方式传递对象至函数。函数参数都是以实际实参的复件为初值,而调用端所获得的是函数返回值的一个附件。这些复件都是由拷贝构造函数产出。看如下例子
代码如下:

class Person{
         public:
             Person();
             virtual ~Person();
             ...
         private:
             std::string name;
             std::string address;
         }

         class Student:public Person{
         public:
             Student();
             ~Student();
             ...
         private:
             std::string schoolName;
             std::string schoolAddress;
         };


那么如果有一个函数验证是否为学生
代码如下:

bool validateStudent(Student s);
Student plato;
bool platoIsOK=validateStudent(plato);  

分析这3行代码,编译器到底做了什么?首先调用Student的copy构造函数,然后以plato为蓝本将s初始化,当validateStudent返回被销毁,所以成本为"一次Student copy构造函数调用,加上一次Student析构函数调用"。
Student对象内部有两个string对象,所以构造了两个string对象。Student继承自Person对象,里面又有两个string对象。所以by value方式传递一个Student对象,总体成本是"六次构造函数和六次析构函数"!

以by reference方式传递参数也可避免对象切割问题。当一个derived class对象以by value方式传递并被视为一个base class对象,base class的copy构造函数会被调用,造成像derived class对象全被切割掉了,仅仅留下base class对象。看如下代码通过传递引用参数完成多态

代码如下:

class Window{
public:
...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars:public Window{
public:
...
virtual void display() const;
};

//传入Windos类型,调用其display函数
//传入WindowWithScrollBars类型,调用其display函数
//体现多态
void printNameAndDispaly(const Window& w)
{
std::cout

    
 
 
 
本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • 写CSS_关于Border你可能需要注意的地方第1/2页
  • spring的事务类型及spring和hibernate可能导致的问题分析
  • 请问怎么对一个数组排序,数组的内容是字符串,可能是单个也可能是多个?
  • 单条汇编语句是否可能因线程切换而被打断?
  • ssh 远程登陆,没反映。可能是什么原因?
  • 比较高级的问题哦,就是不知道可不可能?
  • 虽然可能很少有人知道,但还是要问,高手快来!!!
  • 在一台SUN上用ifconfig看到mac地址是0,不太可能吧
  • 可能的问题??
  • 问题可能有点怪
  • 父进程关闭的同时,有没有可能不关闭子进程?
  • 很奇怪的问题,可能是我的知识面不广。
  • 机子可能被人黑了,怎样才能找回ROOT的密码进去??
  • 任何将内核编译为不支持版本控制可能?
  • 请问exec系列函数在执行时是立即返回还是有可能阻塞那?
  • 装了redhat linux 9 进不去系统 有可能是什么原因
  • 多线程socket申请有无可能产生冲突?
  • 可以ping通路由器,但是ping不通外部地址可能是什么原因?
  • 求牛人centos 5.6 死机 无法进入系统 或提供可能的解决方法
  • 请问调用数据库可更新结果集的resultSet.deleteRow()不成功可能是什么原因?
  • 软硬件速度不匹配时,有没有可能引起重启?
  • 我的SCJP证书没寄到,可能丢了,有谁知道怎么办????70分




  • 特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3