跟我一起复习C++(1)——对象和类

  前言:读完了Multiple View Geometry的第九章和第十章,想着用程序实现下相机恢复和场景重建,上一篇博客已经实现了对相机的恢复,在场景重建编程时无奈发现C++学的并不好,只好翻出来压仓多年的C++ Primer Plus,大家跟我一起复习吧。

  我参考的程序是Mastering OpenCV这本书的第四章例程Structure from Motion的代码,推荐做SLAM的小白也看一下这本书的这一章节。
  
  在运行这个例程时遇到几个问题我都写在之前的一个博客里了,如果大家也遇到了类似的报错可以参考一下:http://blog.csdn.net/fk1174/article/details/51786700,跑通之后就是开始研读源代码了,然后就是一堆诸如此类的。。。

class MultiCameraPnP : public MultiCameraDistance
{
    std::vector<CloudPoint> pointcloud_beforeBA;
    std::vector<cv::Vec3b> pointCloudRGB_beforeBA;

public:
    MultiCameraPnP(const std::vector<cv::Mat>& imgs_,const std::vector<std::string>& imgs_names_,const std::string& imgs_path_):MultiCameraDistance(imgs_,imgs_names_,imgs_path_) {}
...
...
...
}

  作为原来大学学VB的我来说并不是很容易看懂的,在翻开C++这本书前我就给自己提出了两个问题:
  
  1,和类MultiCameraPnP名称一样的成员函数是做什么的?
  2,定义类的时候冒号后面怎么还有一坨东西?class MultiCameraPnP : public MultiCameraDistance

  有了需求再翻开书看是一件不会让人轻易睡着的事情。

  对于C++里面为什么要引入类的概念想必大家都应经听了很多了,这是面向编程最大的特点,我的理解是它是一个对一类东西通用的数据块单元,里面包含了这一类东西所共有的属性,和操作这些属性值的接口,在说类的public和private部分之前我们要有一个概念,虽然平时我们写小程序所有代码都是自己写的,但是当代码工程量很大时就不是一个人写了,这时候就有了代码分工,我们把类设计者和类使用者分开,就有了public和private的概念,比如我是老大,我写的代码负责定义类,我手上有几个小弟负责使用我的类去进一步实现别的功能,那么我肯定不能让我的小弟们随便修改类里面的全部东西,所以有了private的存在,而我得设计出一系列公有接口让小弟们调用,他们才可以通过这些接口修改类里面的某个值。

  类成员包括数据成员和成员函数,他们都可以放在public或者private中声明,一般数据放在私有以满足隐藏数据需要,函数一般放在公有以调用,但是也有放在私有的函数比如好几个成员函数都调用了一个外部并不需要的函数,比如下面的set_tot():

void Stock::acquire(const std::string & co, long n, double pr)
{
    company = co;
    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

void Stock::buy(long num, double price)
{
     if (num < 0)
    {
        std::cout << "Number of shares purchased can't be negative. "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more than you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

  由于set_tot()只是实现代码的一种方式,不是共有接口组成部分,因此声明为私有,即我这个老大可以用,我的小弟不能用,它最大的好处在于确保计算完全相同。

  我们所创建的每个新对象都有自己的存储空间,用于存储其内部变量和类成员,这样的好处是显而易见的,比如一般我们再写程序时会定义一大堆变量,它们大多长得差不多只是结尾数字标号不一样代表是不同对象的某个属性值,管理起来极其繁杂,现在我们有了类,每一个对象只需要定义一次它内部的所有变量就都定义好了,而且都有各自的内存块,比如在ORB_SLAM2这个工程中,主函数只需要定义一个求解器:

    // Create SLAM system. It initializes all system threads and gets ready to process frames.
    ORB_SLAM2::System SLAM(argv[1],argv[2],ORB_SLAM2::System::MONOCULAR,true);

这样程序就会自己定义一大堆内部变量,然后再执行:

SLAM.TrackMonocular(im,tframe);

把图片流传送进入这个求解器就可以了。

  下面咱们看类的构造函数和析构函数。
  构造函数说白了就是定义一个对象时对他进行初始化的函数,为什么不能直接初始化所有变量而非得单独添加一个构造函数呢?看下面的程序:

int year = 2016;       //valid init

struct thing
{
    char* pn;
    int m;
}
thing thing1 = {"aa",20};//valid init

Stock hot = {"Apple",200,1.0};  //invalid init

  第三个的错误原因在于数据是私有的不能直接访问,所以需要一个成员函数也就是构造函数来进行操作。

  现在问题是怎样声明和定义构造函数呢?Stock的构造函数需要为对象提供3个值,因此构造函数需要3个参数,原型如下:

Stock(const string & co, long n = 0, double pr=0.0);

  他的定义如下:

void Stock::Stock(const std::string & co, long n, double pr)
{
    company = co;
    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

  记住,程序声明对象时将自动调用构造函数。

  C++提供了两种使用构造函数来初始化对象的方式:
  1,显式调用:

Stock food = Stock("Apple",200,1.0);

  2,隐式调用:

Stock food("Apple",200,1.0);

  还有一种是构造函数与new一起使用:

Stock *pfood = new Stock("Apple",200,1.0);

  这条语句创建一个Stock对象,初始化为提供的参数,并把地址赋给指针pfood,对象并没有名称,只能使用指针来管理对象。
  当我们一开始并没有提供初始值时,默认构造函数就被用来创建对象了,比如:

#include <iostream>
#include "stock00.h"

int main()
{
    Stock fluffy_the_cat;
    fluffy_the_cat.acquire("NanoSmart", 20, 12.50);
    fluffy_the_cat.show();
    fluffy_the_cat.buy(15, 18.125);
    fluffy_the_cat.show();
    fluffy_the_cat.sell(400, 20.00);
    fluffy_the_cat.show();
    fluffy_the_cat.buy(300000,40.125);
    fluffy_the_cat.show();
    fluffy_the_cat.sell(300000,0.125);
    fluffy_the_cat.show();
    // std::cin.get();
    return 0;
}

  这条语句没错误的原因在于:如果类设计的时候没有设计任何构造函数,C++将自动提供默认构造函数,它是默认构造函数的隐式版本不做任何工作,创建对象并不初始化成员,和下面一样:

int x;

  有一点要记住,只有没设计任何构造函数的时候C++才会提供默认构造函数,为类定义了构造函数后程序员就必须为他提供默认构造函数,如果只提供非默认构造函数比如Stock(const string & co, long n = 0, double pr=0.0);下面的声明就会出错:

Stock food;

  简单点说就是:你要么不提供任何构造函数(C++会给你提供默认构造函数)要么你在写非默认构造函数的同时再写一个默认构造函数(C++不会给你提供默认构造函数了),否则上面的语句会报错!所以说你想创建对象的时候不显式地初始化,必须定义一个不接受任何参数的默认构造函数。但一般来说应初始化多有对象,下面是为Stock类定义的一个默认构造函数:

Stock::Stock()
{
    company ="NO_NAME";
    sharee = 0;
    share_val=0;
    total_val=0;
}

  有了这个默认构造函数,我们就可以在声明对象时不对其进行显式初始化(但是确实初始化了!):

Stock first;               //几个默认值都被设定了
Stock first = Stock;    //几个默认值都被设定了
Stock *pfirst = new Stock; //几个默认值都被设定了

  下面是析构函数,析构函数说白了就是对象用完时释放内存的函数,析构函数原型必须是:

~Stock();

  实现可以是什么都不执行的函数:

Stock::~Stock()
{
}

  好,一开头咱们留的两个问题还记得吗?第一个答案很清楚了,就是构造函数,那第二个呢?它其实叫做Initialization Lists,看下面的程序:
  

class Foo
{
        public:
        Foo() { std::cout << "Foo's constructor" << std::endl; }
};
class Bar : public Foo
{
        public:
        Bar() { std::cout << "Bar's constructor" << std::endl; }
};

int main()
{
        // a lovely elephant ;)
        Bar bar;
}

  创建bar分为两步:先调用Foo的构造函数然后调用Bar的构造函数,执行上面的程序你会看到相同的结果,至于为什么要这样做还请参考大神解析:http://www.cprogramming.com/tutorial/initialization-lists-c++.html
  
  总结:构造函数是创建对象时被调用的函数,和类名相同,通过重载可以创建多个同名的构造函数,不过必须参数列表不同才行;就像创建对象时自动调用构造函数一样,对象被删除时自动调用析构函数,它没有返回类型(连void也没有),名称是“~类名”,如果构造函数使用了new,析构函数必须使用delete。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值