1- Improve Your Source Code
Suggestions to your Project3
- Use
size_t
format.cols
andmat.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
文章来源:https://www.toymoban.com/news/detail-735240.html
文章来源地址https://www.toymoban.com/news/detail-735240.html
到了这里,关于【CPP】继承和虚函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!