【CPP】继承和虚函数

1- Improve Your Source Code

Suggestions to your Project3

  • Use size_t for mat.cols and mat.rows
  • use memcpy() to copy data. Element assignment has a lower efficiency
  • Use 1D array (float *) nor 2D array (float **) for matrix data
  • Redundant computation in loops
  • Do parameter checking in functions: null pointers, dimension matching in matrix operations, etc
  • Do not bind the create matrix function with file I/O
  • File name : head.h, source1.c, source2.c, source3.c. NO!
  • Good implementation VS good homework

matrix.h

#ifndef _MATRIX_H
#define _MATRIX_H

#include "stdbool.h"  // for bool
#include "stdlib.h"

typedef struct Matrix_{  // use typedef to simplify type name
    size_t rows;   // use size_t,  not int
    size_t cols;   // use size_t, not int
    float * data;
}Matrix;


Matrix * createMat(size_t rows, size_t cols);
bool releaseMat(Matrix * p);
bool add(const Matrix * input1, const Matrix * input2, Matrix *output);

#endif

matrix.c

#include <stdlib.h>
#include <stdio.h>
#include "matrix.h"

// return NULL if failed
Matrix * createMat(size_t rows, size_t cols)
{
    Matrix * p = NULL;
    
    if (rows == 0 || cols == 0)
    {
        fprintf(stderr, "rows and/or cols is 0.\n");  // 错误打印到标准错误输出,可以通过管道把错误信息导到不同的文件
        return NULL;
    }

    // allocate memory
    p = (Matrix *) malloc(sizeof(Matrix));
    if (p == NULL)
    {
        fprintf(stderr, "Failed to allocate memory for a matrix.\n");
        return NULL;
    }

    p->rows = rows;
    p->cols = cols;
    p->data = (float *) malloc(p->rows * p->cols * sizeof(float));

    if (p->data == NULL)
    {
        fprintf(stderr, "Failed to allocate memory for the matrix data. \n");
        free(p);  // Don't forget to free memory here;
        return NULL;
    }

    return p;
}

bool releaseMat(Matrix * p)
{
    // don't forget to check a pointer before using it
    if (!p) return false;

    if (p->data) free(p->data);

    free(p);

    return true;
}

bool add(const Matrix * input1, const Matrix * input2, Matrix * output)
{
    // You must check all parameters carefully first
    // It's important,  and can save a lot of time on debugging
    if (input1 == NULL)
    {
        // use stderr for error messages
        fprintf(stderr, "File %s, Line %d, Function %s(): The 1st parameter is NULL. \n",
        __FILE__, __LINE__, __FUNCTION__);
        return false;
    }
    else if (input1->data == NULL)
    {
        fprintf(stderr, "%s(): The 1st parameter has no valid data. \n", __FUNCTION__);
        return false;
    }

    if (input2 == NULL)
    {
        // use stderr for error messages
        fprintf(stderr, "File %s, Line %d, Function %s(): The 2nd parameter is NULL. \n",
        __FILE__, __LINE__, __FUNCTION__);
        return false;
    }
    else if (input2->data == NULL)
    {
        fprintf(stderr, "%s(): The 2nd parameter has no valid data. \n", __FUNCTION__);
        return false;
    }

    if (output == NULL)
    {
        // use stderr for error messages
        fprintf(stderr, "File %s, Line %d, Function %s(): The 3rd parameter is NULL. \n",
        __FILE__, __LINE__, __FUNCTION__);
        return false;
    }
    else if (output->data == NULL)
    {
        fprintf(stderr, "%s(): The 3rd parameter has no valid data. \n", __FUNCTION__);
        return false;
    }

    if (input1->rows != input2->rows || input2->rows != output->rows || 
        input1->cols != input2->cols || input2->cols != output->cols)
    {
        fprintf(stderr, "The input and the output do not match. They should have the same size. \n");
        fprintf(stderr, "Their sizes are(%zu, %zu), (%zu, %zu) and (%zu, %zu)", 
                input1->rows, input1->cols,
                input2->rows, input2->cols,
                output->rows, output->cols);
        
        return false;
    }

    // version1, the best one
    size_t length = input1->rows * input1->cols;
    const float * p1 = input1->data;
    const float * p2 = input2->data;
    float *p3 = output->data;
    for(size_t i = 0; i<length; i++)
    {
        *(p3++) = *(p1++) + *(p2++);
    }
    
    // version2, better one
    for (size_t r = 0; r < input1->rows; r++)
    {
        // to calculate (col*r) herre, don't put it into the inner loop
        const float *p1 = input1->data + input1->cols * r;
        const float *p2 = input2->data + input2->cols * r;
        float *p3 = output->data + output->cols * r;

        for (size_t c = 0; c < input1->cols; c++)
        {
            *(p3++) = *(p1++) + *(p2++);
        }
    }


    // version3, a bad one
    for(size_t r = 0; r < input1->rows; r++)
    {
        for (size_t c = 0; c < input2->cols; c++)
        {
            output->data[output->cols * r + c] = 
            input1->data[input1->cols * r + c] +
            input2->data[input2->cols * r + c]; 
        }
    }
    return true;
}

main.c

#include <stdio.h>
#include "matrix.h"


int main()
{
    Matrix * matA = createMat(2, 3);
    Matrix * matB = createMat(2, 3);
    Matrix * matC = createMat(2, 3);
    Matrix * matD = createMat(3, 2);
    Matrix * matNULL = NULL;

    //initialization
    // You should have your own method to do it
    matA->data[3] = 2.3f;
    matB->data[3] = 3.1f;


    if( !add(matA, matB, matC))
    {
        fprintf(stderr, "Mayrtix addition failed.");
    }
    else{
        // You can have a metter method to show the results
        printf("result=%f\n", matC->data[3]);
    }

    // more tests
    add(matA, matB, matD);

    add(matNULL, matB, matC);

    return 0;


}
gcc *.c
./a.out
result=5.400000
The input and the output do not match. They should have the same size. 
Their sizes are(2, 3), (2, 3) and (3, 2)File matrix.c, Line 58, Function add(): The 1st parameter is NULL.

2-Derived Class

Inheritance

  • Inherit members (attributes and functions) from one class

(1) Base class (parent)
(2) Derived class (child)

class Base
{
  public:
    int a;
    int b;
};

class Derived: public Base
{
  public:
    int C;
};
  • C++ supports multiple inheritance and multilevel inheritance
    多个继承:
class Derived: public Base1, public Base2
{
  ...
};

Constructors

构造函数在子类中怎么执行

  • To instantiate a derived class object

(1) Allocate memory
(2) Derived constructor is invoked:

[1] Base object is constructed by a base constructor
[2] Member initializer list initializes members
[3] To execute the body of the derived constructor

class Derived: public Base
{
  public:
    int c;
    Derived(int c): Base(c - 2, c - 1), c(c)
    {
      ...
    }
};

Destructors

  • The destructor of the derived class in invoked first
  • Then the destructor of the base class

Example

derived.cpp

#include <iostream>
using namespace std;

class Base
{
  public:
    int a;
    int b;
    Base(int a = 0, int b = 0)
    {
        this->a = a;
        this->b = b;
        cout << "Constructor Base::Base(" << a << ", " << b << ")" << endl;
    }
    ~Base()
    {
        cout << "Destructor Base::~Base()" << endl;
    }
    int product()
    {
        return a * b;
    }
    friend std::ostream & operator<<(std::ostream & os, const Base & obj)
    {
        os << "Base: a = " << obj.a << ", b = " << obj.b;
        return os;
    }
};

class Derived: public Base
{
  public:
    int c;
    Derived(int c): Base(c - 2, c - 1), c(c)
    {
        this->a += 3; //it can be changed after initialization
        cout << "Constructor Derived::Derived(" << c << ")" << endl;
    }
    ~Derived()
    {
        cout << "Destructor Derived::~Derived()" << endl;
    }
    int product()
    {
        return Base::product() * c;
    }
    friend std::ostream & operator<<(std::ostream & os, const Derived & obj)
    {
        // call the friend function in Base class
        os << static_cast<const Base&>(obj) << endl;

        os << "Derived: c = " << obj.c;
        return os;
    }
};

int main()
{
    {
        Base base(1, 2);
        cout << "Product = " << base.product() << endl;
        cout << base << endl;
    }
    cout << "----------------------" << endl;
    {
        Derived derived(5);
        cout << derived << endl;
        cout << "Product = " << derived.product() << endl; 
    }
    return 0;
}

Constructor Base::Base(1, 2)
Product = 2
Base: a = 1, b = 2
Destructor Base::~Base()
----------------------
Constructor Base::Base(3, 4)
Constructor Derived::Derived(5)
Base: a = 6, b = 4
Derived: c = 5
Product = 120
Destructor Derived::~Derived()
Destructor Base::~Base()

3- Access Control

Member Access

  • Public members
    Accessible anywhere
  • Private members
    Only accessible to the members and friends of that class
class Person{
  private:
    int n;  // private member
   
  public:
    // this->n is accessible
    Person() : n(10) {}     // initialization 
    // other.n is accessible
    Person(const Person& other): n(other.n) {}   // Copy constructor
    // this->n is accessible 
    void set(int n) {this->n = n;}
    // this->n and other.n are accessible 
    void set(const Person& other) {this->n = other.n; }
};
  • Protected members

Accessible to the members and friends of that class

class Base
{
  protected:
    int n;
  private:
    void fool(Base& b)
    {
      n++;   // Okay
      b.n++;   // Okay
    }
};

Accessible to the members and friends of the derived class

class Derived : public Base
{
  void foo2(Base& b, Derived& d)
  {
    n++;   // Okay
    this->n++;   // Okay
    b.n++;    // Error
    d.n++;   // Okay
  }
};


// a non-member non-friend function
void compare(Base& b, Derived& d)
{
  b.n++; // Error
  d.n++;  // Error
}

Public Inheritance

  • Public members of the base class

(1) Still be public in the derived class
(2) Accessible anywhere

  • Protected members of the base class

(1) Still be protected in the derived class
(2) Accessible in the derived class only

  • Private members of the base class

(1) Not accessible in the derived class

可以通过父类公有函数去访问

Protected Inheritance

  • Public members and protected members of the base class

(1) Be protected in the derived class
(2) Accessible in the derived class only

  • Private members of the base class

(1) Not accessible in the derived class

Private Inheritance

  • Public members and protected members of the base class

(1) Be private in the derived class
(2) Accessible in the derived class only

  • Private members of the base class

(1) Not accessible in the derived class

4- Virtual Functions

Virtual Functions

  • Let’s look at the example first, what will be the output?
class Person
{
  public:
    void print()
    {
      cout << "Name: " << name << endl;
    }
};
class Student: public Person
{
  public:
    void print()
    {
      cout << "Name: " << name;
      cout << ". ID: " << id << endl;
    }
};

Person *p = new Student();
p->print();   // call Person:print()?

子类和父类有同样的函数,调用子类的对象的函数,会调用子类还是父类定义的函数?

Example

virtual.cpp

#include <iostream>
#include <string>
using namespace std;

class Person
{
  public:
    string name;
    Person(string n): name(n){}
    void print()
    {
        cout << "Name: " << name << endl;
    }
};


class Student: public Person
{
  public:
    string id;
    Student(string n, string i): Person(n), id(i){}
    void print() 
    {
        cout << "Name: " << name;
        cout << ". ID: " << id << endl;
    }
};

void printObjectInfo(Person & p)
{
    p.print();
}

int main()
{
    {
        Student stu("yu", "2019");
        printObjectInfo(stu);  
    }

    {
        Person * p = new Student("xue", "2020");
        p->print(); //if print() is not a virtual function, different output
        delete p; //if its destructor is not virtual
    }
    return 0;

Name: yu
Name: xue 

调用的都是父类的函数,而这个对象是子类的对象,我们希望对于子类的对象调用子类定义的函数,结果还是调用的父类的函数

  • 在函数前加virtual就可以实现多态
#include <iostream>
#include <string>
using namespace std;

class Person
{
  public:
    string name;
    Person(string n): name(n){}
    void print()
    {
        cout << "Name: " << name << endl;
    }
};


class Student: public Person
{
  public:
    string id;
    Student(string n, string i): Person(n), id(i){}
    void print() 
    {
        cout << "Name: " << name;
        cout << ". ID: " << id << endl;
    }
};

void printObjectInfo(Person & p)
{
    p.print();
}

int main()
{
    {
        Student stu("yu", "2019");
        printObjectInfo(stu);  
    }

    {
        Person * p = new Student("xue", "2020");
        p->print(); //if print() is not a virtual function, different output
        delete p; //if its destructor is not virtual
    }


    return 0;
}
Name: yu. ID: 2019
Name: xue. ID: 2020

为什么?

Virtual Functions

  • But if we define print() function as a virtual function, the output will be different
  • Static binding: the compiler decides which function to call
  • Dynamic binding: the called function is decided at runtime
  • Keyword virtual makes the function virtual for the base and all derived classes
  • 虚函数与非虚函数的区别:绑定的区别;非虚函数是静态绑定的,虚函数是动态绑定的
  • 静态绑定:编译器在编译源代码时就决定了在这里应该执行哪个函数
  • 动态绑定:在编译时不决定应该执行哪个函数,而是在运行时根据传进来的参数来决定执行哪个函数
  • 一旦定了虚函数,对象里面第一个成员变量是一个隐含的成员变量,这个变量会指向一个虚表,这个虚表会指向对应的对象类的函数。

参考:C++虚函数表剖析

  • 纯虚类函数:没有定义,只是一个接口。这个类纯虚的类,不能创建对象
class Person2
{
  public:
    string name;
    Person2(string n): name(n){}
    virtual void print() = 0; 
};
  • If a virtual destructor is not virtual, only the destructor of the base class is executed in the follow examples
Person * p = new Student("xue", "2020");
p->print();
...
...
delete p; // if its destructor is not virtual 

析构函数一定是虚函数

5-Inheritance and Dynamic Memory Allocation

Question

  • If a base class uses dynamic memory allocation, and redefines a copy constructor and assignment operator
  • Case 1: If no dynamic memory allocation in the derived class, no special operations are needed
  • Case2: If dynamic memory is allocated in the derived class, you should redefine a copy constructor and an assignment operator

class MyMap: public MyString
{
  private:
    char * keyname;
    MyMap(const char * key, const char * value)
    {
        ...
    }
    MyMap(const MyMap & mm): MyString(mm.buf_len, mm.characters)
    {
        //allocate memory for keyname
        //and hard copy from mm to *this
    }
    MyMap & operator=(const MyMap &mm)
    {
        MyMap::operator=(mm);
        //allocate memory for keyname
        //and hard copy from mm to *this
        return *this;
    }

};

6-Examples in OpenCV

Derived cv::Mat_

  • Template matrix class derived from cv::Mat, a wrapper, more C++ style

在这里插入图片描述

  • Mat_ 类比Mat类更加C++化

cv::Matx

  • A template class for small matrices whose type and size are known at compilation time

在这里插入图片描述

  • 针对小矩阵的设计的

cv::Vec

在这里插入图片描述

在这里插入图片描述

Combined with typedef

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值