QT软件实习笔记2

2024.1.14

上午:

1.昨天作业评讲:

1. 重载Student结构中的输入和输出运算符;

在对某个类或结构进行运算符重载时,要根据实际情况选择要重载的运算符;

struct Student {
    int id;
    char name[41];
    int age;
    int scores[3];
    friend ostream &
    operator<<(ostream &out, const Student &obj) {
        /*return out << "Student{" << obj.id << ",\"" << obj
            .name << "\"," << obj.age << ",{" << obj.scores[0]
                   << "," << obj.scores[1] << ","
                   << obj.scores[2] << "}}";*/
        out << "Student{" << obj.id;
        out << ",\"" << obj.name << "\",";
        out << obj.age << ",{";
        out << obj.scores[0] << "," << obj.scores[1] << ","
            << obj.scores[2] << "}}";
        return out;
    }
    friend istream &operator>>(istream &in, Student &obj) {
        cout << "input id: ";
        in >> obj.id;
        in.get();
        cout << "input name: ";
        in.getline(obj.name, sizeof(obj.name));
        cout << "input age: ";
        cin >> obj.age;
        cout << "input three scores: ";
        in >> obj.scores[0] >> obj.scores[1] >> obj.scores[2];
        return in;
    }
};
QT软件实习笔记2_第1张图片
2. 重载Value结构的+、*、>、<这四个运算符;

1. 针对以上的四个运算符,都是两个对象的运算;v1+v2, v1*v2,v1>v2,v1

2. 重载时,尽量使用友元函数的重载;所谓的友元是站在朋友的立场上,对v1和v2进行操作;

3. 还可使用成员函数进行重载时,没有友元函数重载更加直观和清晰;

4. 在类或结构中,针对特定运算符重载后,在操作对象间的运算时,就像操作内置类型变量的运算一样,比较直观;12+12  v1+v2  v1.add(v2);

5.  在运算符重载这一知识点,在所有的编程语言中,只有C++有这个功能;

struct Value {
    int value;
    friend ostream &operator<<(ostream &out, const Value &obj) {
        out << "Value{" << obj.value << "}";
        return out;
    }
    friend istream &operator>>(istream &in, Value &obj) {
        in >> obj.value;
        return in;
    }
    friend Value operator+(const Value &v1, const Value &v2) {
        return Value{v1.value + v2.value};
    }
    friend Value operator*(const Value &v1, const Value &v2) {
        return Value{v1.value * v2.value};
    }
    friend bool operator>(const Value &v1, const Value &v2) {
        return v1.value > v2.value;
    }
    friend bool operator<(const Value &v1, const Value &v2) {
        //return v1.value < v2.value;
        return !(v1 > v2);
    }
};
void test() {
    Value v1{14};
    Value v2{24};
    Value vsum = v1 + v2;
    Value vtimes = v1 * v2;
    bool greater = v1 > v2;
    cout << vsum << endl;
    cout << vtimes << endl;
    cout << greater << endl;
    bool less = v1 < v2;
    cout << less << endl;
    bool equals_greater = v1 != v2;
   
}

     * 当在Value中重载了大于运算符时,编译器就知道了如何比较
     * 前者大于后者;但并不知道前者如何小于后者;
     * 即知道如何比较 10 > 5,但不知道如何比较 5 < 10;

2. 指针指向数组

1. 指针指向静态数组;

在栈区中分配内存的数组,这种数组是由编译器自动分配内存,自动回收,整个过程不需要程序员干预,只使用即可;这种数组存活均在栈区中;

2. 指针指向动态数组;

在堆区存活的数组;需要向堆区中申请内存来存放数组中的所有元素,使用前需要检测内存是否申请成功,使用完后,还需要释放内存,返还堆区;

3. 在线测试指针指向数组的功能

https://pythontutor.com/visualize.html#mode=edit

示例:在下面所示区域输入代码,完成后点击“Visualize Execution”(即将所输入代码可视化执行)

QT软件实习笔记2_第2张图片

点击过后出现下面右边区域,持续点击Next可观察到整个代码流程

QT软件实习笔记2_第3张图片QT软件实习笔记2_第4张图片QT软件实习笔记2_第5张图片

show memory addresses(显示内存地址)

QT软件实习笔记2_第6张图片

byte-level view of data(数据的字节级视图)

QT软件实习笔记2_第7张图片

⚠️注意:申请时对应删除

4.示例代码:

指向一个动态数组:

#include 
using namespace std;
int main {
int number = 100;
cout<< "number = "<

指向一个静态数组:

#include 
2	using namespace std;
3	int main() {
4	
5	  int array[]{11,2,3,4,100,-7,};
6	  int nn=sizeof(array)/sizeof(*array);
7	  for(int i=0;i"<

QT软件实习笔记2_第8张图片

在以上代码中,涵盖了C++中多种可能出现变量方式;

(1)、x; 全局变量;在整个文档中有效;

(2)、array|nn; 局部变量(函数中定义的变量称为局部变量),在函数中,从定义时起到整个函数结束;

(3)、i;块变量,只在定义它的块中有效;

#include 
using namespace std;
int x=11;
int main() {    
  int array[]{11,2,3,87,5,};
  int nn = sizeof(array)/sizeof(*array);
  for(int i=0;i"< "<<*ptr++<

QT软件实习笔记2_第9张图片

  在C语言和C++中,存活于栈区中的数组,数组名就是一个不带 * 号的常量指针名;它保存了数组元素0的地址;

表示地址时:&array[0] == array ;

访问时:array[0] == *array

注意:在说到数组元素时,不要说:第3个元素;而要说:元素2;对于数组的首元素,要说:元素0,不要说:第一个元素;(即注意⚠️地址下标从0开始)

为什么说数组名是一个常量指针:因为一个数组一旦确定,数组名与它所表示的数组永远绑定,其内存区域永不可改!

int *ptr = array;

上述代码表示:一个变量指针ptr,指向了常量指针array,对于数组的元素操作,除了array外,还可用ptr操作;ptr可以依次指向数组中的不同元素。

3. 测试存活于堆区的带指针分量的结构或类

强调:这样的结构或类对象,只须活动于内存中,不能写到文件里;无论C语言还是C++,基本操作中不能将指针指向的数组写入到文件中并取出来;

1、 申请内存,用来存放结构对象;

2、 分别申请内存,用来存放分量内容;

3、 每次申请内存,都要进行检测;

4、 使用堆区内存;

5、 使用堆区内存完毕,要释放内存,释放时要注意释放顺序;

6、 顺序原则:先申请的后释放;

7、 具体来说,先释放分量内存,没有顺序;

#include 
#include 
#include 
#include 
using namespace std;
void test();
int main() {
    printf("\n");
    test();
    printf("\n");
    return 0;
}
struct Person {
    char *pid;  //身份证号
    int age;  // 年龄 
    char *address;// 地址
};
void test() {
    Person *pst = nullptr;
    //向堆区申请一块内存,用来存放一个Person对象;
    pst = new Person;
    if (!pst) {
        cout << "内存申请失败";
        return;
    }
    pst->age = 23;
    pst->pid = new char[9];
    //检测略;
    strcpy(pst->pid, "abcd1234");
    pst->address = new char[25];
    //检测略;
    strcpy(pst->address, "中国 山东 青岛");
    printf("\n");
    cout << "id: " << pst->pid << endl;
    cout << "age: " << pst->age << endl;
    cout << "addr: " << pst->address << endl;
    delete []pst->pid;
    delete []pst->address;
    delete pst;
    pst = nullptr;    
}

QT软件实习笔记2_第10张图片

⚠️堆区中的内存必须释放

最后的安全结果:

QT软件实习笔记2_第11张图片

在C语言或C++中,对于内存的使用遵循一个大的原则:

      尽量少使用栈区内存,尽量多使用堆区内存;

扩展

设置一个简单的结构,可以只有一个分量指针,这个指针分量可以是Char*的,也可以是内置类型的;在main函数中,创建一个Test类型的指针,它要向堆区申请内存,存放3个对象;包括赋值和输出; 编码实现以下功能;

QT软件实习笔记2_第12张图片

代码完成:(⚠️代码还没交由老师检查,不能保证其正确性):

#include 
using namespace std;

struct Test {
    int id;
    char* name;
    int* scores;
};

int main() {
    // 创建Test类型的指针
    Test* testPtr = new Test;

    // 向堆区申请内存,存放3个对象
    testPtr->id = 1;
    testPtr->name = new char[10];
    strcpy(testPtr->name, "John");
    testPtr->scores = new int[3];
    testPtr->scores[0] = 90;
    testPtr->scores[1] = 85;
    testPtr->scores[2] = 95;

    // 输出对象的信息
    cout << "ID: " << testPtr->id << endl;
    cout << "Name: " << testPtr->name << endl;
    cout << "Scores: ";
    for (int i = 0; i < 3; i++) {
        cout << testPtr->scores[i] << " ";
    }
    cout << endl;

    // 释放内存
    delete[] testPtr->name;
    delete[] testPtr->scores;
    delete testPtr;

    return 0;
}

下午:

4. 指针指向函数;

1、 通过函数隶属分类(就是函数的主人是谁)
i. 内置函数(预定义函数)

也即是头文件提供的函数;printf,scanf,getchar等;直接应用于程序中的任何角落;

ii. 自定义函数

1.  声明;声明于调用函数(多为main函数)之前;也叫函数头或函数原型;声明不需要参数名,有参数类型即可;当然参数也可有名;

2.  定义;定义在调用函数之后;函数功能的具体实现,参数必须有名字;

3.  应用;在调用函数中调用函数;

void hello(const char *);
void print(int);
void test() {
    print(11);
    hello("hello,gcc!!");
}
void print(int a) {
    cout << "a -> " << (a) << endl;
}
void hello(const char *str) {
    cout << "str -> " << (str) << endl;
}

2. 通过函数的参数返回值类型分类

i. 无参、无返回值的函数;void print();

ii. 无参、有返回值的函数;int *get();

iii. 有参、无返回值的函数;void print(const char*);

iv. 有参、有返回值的函数;int add(int,int);

3. 当多个自定义函数进行交互(调用)时,声明尤为重要
void f0();
void f1();
void f2();
void test() {
   }
void f0() {
    f2();
}
void f2() {
    printf("******  hello,f2() ******\n");
}
void f1() {
    f2();
}
4. 根据函数是否循环调用进行分类 (37.44)

i. 普通函数;

ii. 递归函数;自己调用自己;在实现数据结构时,极为常用;创建二叉树;

5. 函数指针的概念;

1. 以运算符重载为例说明“类比”;
2. 函数指针是指针的第三个作用,就是一个指针变量,用来指向某个函数,就是保存了这个函数的地址;

  从某个角度上说,函数也是一个指针类型,它和数组一样,也是个不带 * 号的常量指针;

  在C语言和C++中,只有两个内置的常量指针,就是数组名和函数名

#include 
using namespace std;
int add(int, int);
void hello();
void test() {
    cout << "  add -> " << (add) << endl;
    cout << "hello -> " << (hello) << endl;
    printf("\n");
    int aa[]{11, 2, 3,};
    double dd[]{3.14, 2.36};
    cout << "aa -> " << (aa) << endl;
    cout << "dd -> " << (dd) << endl;
}
int add(int a, int b) {
    return a + b;
}
void hello() {
    printf("******  hello,gcc! ******\n");
}
3. 面向对象编程中函数的分类;

i. 重载函数;C语言中没有这样的函数;C++才出现;思考:何为重载函数?

ii. 重写函数;出现在继承链中;

iii. 同类函数;在同一个范围(一个源文件,一个类或结构)内的多个函数,具有:返回值类型相同,参数列表相同,函数名不同的函数,称为“同类函数”;

int add(int,int);

int sub(int,int);

int times(int,int);

void print();

void hello();

4. 同类函数的“类型”如可设置?

     将某类函数“抽象”为某种数据类型!,就是归纳总结出来函数类型;一旦有了某类函数的数据类型,就应该可以定义变量;既然定义了变量,就可以赋值;

  同一范围,多个函数符合:返回值类型相同,参数列表相同,但函数名一定不同;

​​​​​​​

5. 函数类型的抽象:

用到了C语言中提供的关键字:typedef或C++提供的关键字: using;

#include 
#include 
#include 
#include 
using namespace std;
void test();
int main() {
    printf("\n");
    test();
    printf("\n");
    return 0;
}
int sub(int, int);
typedef int (*PtrFunc)(int, int);  //PtrFunc即为此类函数类型名
int add(int, int);
int times(int, int);
int two(int, int);
void test() {
    //定义一个指针变量,分别指向两个同类函数:add和times;
    /*PtrFunc pf = nullptr;
    pf = add;
    auto rr = pf(10, 20);
    cout << "rr -> " << (rr) << endl;
    printf("\n");
    pf = times;
    rr = pf(10, 20);
    cout << "rr -> " << (rr) << endl;
    printf("\n");
    pf = sub;
    rr = pf(10, 20);
    cout << "rr -> " << (rr) << endl;*/
    PtrFunc ff[]{add, sub, times, two,};
    int nn = sizeof(ff) / sizeof(*ff);
    //sizef不是函数,是 个运算符
    for (int i = 0; i < nn; ++i) {
        auto rr = ff[i](100, 20);
        cout << "rr -> " << (rr) << endl;
        printf("\n");
    }
}
int add(int a, int b) {
    printf("******  加法 ******\n");
    return a + b;
}
int times(int a, int b) {
    printf("******  乘法 ******\n");
    return a * b;
}
int sub(int a, int b) {
    printf("******  减法 ******\n");
    return a - b;
}
int two(int a, int b) {
    printf("******  参数平方和 ******\n");
    return a * a + b * b;
}

 QT软件实习笔记2_第13张图片

6.课堂练习

有一个函数:void apply(int *array,int nn,int (*func)(int));其中参数1和参数2为一个整型数组和数组的长度,参数3为对数组元素的操作。要求自行提供数据,实现这个函数的以下几个功能;

1、 将每个元素值扩大 2 倍;

2、 将每个元素值缩小 3 倍;

3、 将每个元素值平方;

作业思考:

1. 当数组做函数参数时,在C语言或C++中为何必须传递数组的长度?

当数组名作函数参数时,它会自动进行了类型退化,变成了一个普通的指针,不再代表整个数组了;如何访问操作数组中的每个元素,如何循环?这时就需要传递一个整数,表示数组长度;

#include 
#include 
#include 
#include 
using namespace std;
void test();
int main(int argc, char *argv[]) {
    printf("\n");
    test();
    printf("\n");
    return 0;
}
void apply(int *array, int nn, int (*func)(int));
int two(int elem);
int divideByThree(int elem);
int square(int elem);
void test() {
    int aa[]{11, 2, 3, 47, 100, 78,};
    int nn = sizeof aa / sizeof *aa;
    printf("******  源数组 ******\n");
    for (int i = 0; i < nn; ++i) {
        cout << setw(5) << aa[i];
    }
    printf("\n\n");
    apply(aa, nn, two);
    printf("******  2倍之后 ******\n");
    for (int i = 0; i < nn; ++i) {
        cout << setw(5) << aa[i];
    }
    printf("\n\n");
    apply(aa, nn, divideByThree);
    printf("******  2倍之后 ******\n");
    for (int i = 0; i < nn; ++i) {
        cout << setw(5) << aa[i];
    }
    printf("\n\n");
    apply(aa, nn, square);
    printf("******  2倍之后 ******\n");
    for (int i = 0; i < nn; ++i) {
        cout << setw(5) << aa[i];
    }
}
void apply(int *array, int nn, int (*func)(int)) {
    if (!array || nn < 1) {
        return;
    }
    for (int i = 0; i < nn; ++i) {
        array[i] = func(array[i]);
    }
}
int two(int elem) {
    return 2 * elem;
}
int divideByThree(int elem) {
    return elem / 3;
}
int square(int elem) {
    return elem * elem;
}
2. 根据函数指针使用函数数组;
using PtrFunc = int (*)(int);
void test() {
    int aa[]{11, 2, 3, 47, 100, 78,};
    int nn = sizeof aa / sizeof *aa;
    printf("******  源数组 ******\n");
    for (int i = 0; i < nn; ++i) {
        cout << setw(5) << aa[i];
    }
    printf("\n");
    printf("\n");
    string descriptions[]{
        "******  2倍后 ******",
        "******  缩小3倍后 ******",
        "****** 平方后 ******",
    };
    PtrFunc ff[]{two, divideByThree, square,};
    int length = sizeof ff / sizeof *ff;
    for (int i = 0; i < length; ++i) {
        apply(aa, nn, ff[i]);
        cout << descriptions[i] << endl;
        for (int i = 0; i < nn; ++i) {
            cout << setw(5) << aa[i];
        }
        printf("\n");
        printf("\n");
    }
}
3. 示例分析;

1.  以往我们使用函数时,只须考虑函数的声明、定义及使用三项;在常规应用中,参数参数多为内置类型及数组这样的操作;

2.  学了函数指针后,指针除了指向简单变量、数组后,还可操作函数,还可以做为另一个函数中的参数;一个函数中,可以有一到多个函数做为参数;

4. 学习C语言的几个难点;

1.  指针概念及应用;

2.  二进制文件的操作;

3. 链表;

5. 学习C++的几个难点;

1.  类的几个默认操作;

2.  运算符的重载;这个功能只有C++有;直观!

3. STL;数据结构的实现;

4.  文件操作;

你可能感兴趣的:(qt,笔记,数据库,学习)