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

下面详细介绍

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


代码如下:

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<<w.name();
w.display();
}

窥视c++编译器的底层,reference往往以指针实现出来,因此pass by reference真正传递的是指针。如果对象属于内置型,pass by value往往比pass by reference 效率高些。

(0)

相关推荐

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

    下面详细介绍 一.初始化与初始赋值 首先说说类的初始化与初始赋值之前的区别,这也许里面可能有我们不知道的事情. 其实类初始化与初始赋值还是有区别的. 复制代码 代码如下: 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,i

  • JavaScript基础语法让人疑惑的地方小结

    复制代码 代码如下: /* JS基础语法中,几个比较容易让人疑惑的地方. */ /* == === */ function de() { var ab = 25; //数值 var ba = "25"; //字符串 if (ab == ba) { //==,会先转换,后比对. //alert("b"); } if (!(ab === ba)) { //===不会转换,而进行直接比对,ab是数值类型数据,而ba是字符串类型数据 //alert("a"

  • javascript中使用css需要注意的地方小结

    1.在改变单个元素样式时,注意style对象的语法和css中使用的语法几乎是一一对应的.不过包含连字符的属性则被替换为一种"camel castring"的形式,例如:font-size现在成了fontSize,而margin-top变成了marginTop: 2.在使用"float"时,因为"float"是javascript的一个保留字,所以就不能使用style.float,而改成了style.cssFloat(IE使用的是style.sty

  • Web2.0下XHTML+CSS 设计需要注意的地方小结

    注意事项: 1.除选择DOCTYPE之外的语句必须使用小写英文字母书写.其中包括 Macromedia Dreamweaver 生成的鼠标动作,如 OnMouseOver 也必须修改成 onmouseover. 2.XHTML语法规要求所有的标识都必须有开始和结束.例如<body>和</body>.<p>和</p>等,对于不成对的标识,要求在标识最后加一个空格,然后跟一个"/".例如<br>写成<br />.<

  • 猜你不知道Spring Boot的几种部署方式(小结)

    引言 本文主要讲的是spring boot的五种部署方式,里面是否有你不知道的呢,如果有欢迎评论留言哦,一起交流探讨哦!!! 可以使用各种方法将Spring Boot应用程序部署到生产系统中.在本文中,我们将通过以下5种方法逐步部署Spring Boot应用程序: 在Java Archive(JAR)中作为独立应用程序进行部署, 将Web应用程序存档(WAR)部署到servlet容器中, 在Docker Container中部署, 在NGINX Web服务器后面部署 - 直接设置, 部署在NGI

  • apache+php+mysql安装配置方法小结

    整个安装流程如下: 1,首先安装apache:我安装的版本是: httpd-2.2.16-win32-x86-openssl-0.9.8o.msi 网址:http://www.apache.org/dist/httpd/binaries/win32/ apache安装和配置比较顺利,没什么好说的. 更改文件路径的方法: 在文件的安装目录下,我的是:D:\Program Files\Apache Software Foundation\Apache2.2\conf,打开httpd.conf搜索:D

  • win10家庭版安装docker遇到的问题小结

    docker下载地址:http://get.daocloud.io/#install-docker-for-mac-windows 1.首先我直接下载了docker for windows,安装时才发现win10家庭版不支持hyper-v功能,只有专业版才有,不能直接安装docker,需要通过docker toolbox安装,于是乎去下载了docker toolbox再来安装 2.安装时有一串可选安装列表,因为我已经安装了git,就没有勾选git for windows,安装完成后启动docke

  • 不知道这5种下划线的含义,你就不算真的会Python!

    什么是 Python? Python 之父 Guido van Rossum 说:Python是一种高级程序语言,其核心设计哲学是代码可读性和语法,能够让程序员用很少的代码来表达自己的想法. 对于我来说,学习 Python 的首要原因是,Python 是一种可以优雅编程的语言.它能够简单自然地写出代码和实现我的想法. 另一个原因是我们可以将 Python 用在很多地方:人工智能.数据科学.Web 开发和机器学习等都可以使用 Python 来开发. 国庆期间后台有小伙伴留言问我:"Python变量

  • 你可能不知道的Python 技巧小结

    译者 | 豌豆花下猫 声明 :本文获得原作者授权翻译,转载请保留原文出处,请勿用于商业或非法用途. 有许许多多文章写了 Python 中的许多很酷的特性,例如变量解包.偏函数.枚举可迭代对象,但是关于 Python 还有很多要讨论的话题,因此在本文中,我将尝试展示一些我知道的和在使用的,但很少在其它文章提到过的特性.那就开始吧. 1.对输入的字符串"消毒" 对用户输入的内容"消毒",这问题几乎适用于你编写的所有程序.通常将字符转换为小写或大写就足够了,有时你还可以使

  • IntelliJ IDEA2020.3 新特性(小结)

    北京时间2020年12月1日,JetBrain公司推出了二十周年第三个稳定版的IntelliJ IDEA 2020.3 . 距离上一个稳定版IDEA,过去了小五个月,这次的更新来得比较晚,我的博客也是.如果不是同事提醒,我也差点忘记了自己其实是打算把IDEA编辑器这个系列给一直更新下去的哈哈哈,话不多说,让我们先进官网看看. 以下是官网的原话: IntelliJ IDEA 2020.3 adds interactive hints and inline watches in the debugg

随机推荐