推广 热搜: csgo  vue  2023  angelababy  gps  信用卡  新车  htc  落地  控制 

怎样才算学会了C++基础,一篇文章学习了解!

   2023-08-02 网络整理佚名1700
核心提示:C++中,引用和指针都可以用来间接访问变量,但它们之间有以下区别:下面是一个例子,演示了引用和指针的使用:在函数参数中,引用和指针都可以用来传递参数,但它们之间也有一些区别:下面是一个例子,演示了引用和指针作为函数参数的使用:以上的内容都是简单的罗列,具体的内容还需看书本或者视频,如果是复习可以参考此文章,学习C++不是一蹴而就的,是终身的,基础部分十分的重要,需要反复的学习和体会。

由于公众号推送规则发生变更,请点击“观看”并加“星”以第一时间获得精彩技术分享

源自网络,侵删

内容:C++基础、STL、数据结构、TCP/IP、数据库、linux、QtC++(推荐两本书C++和C++ plus)语法部分

C++的基本语法包括以下几个方面:

笔记

C++支持单行注释(以“//”开头)和多行注释(以“/”开头并以“/”结尾)。

标识符

标识符是变量、函数、类、结构等的名称。标识符必须以字母或下划线开头,后面可以跟字母、数字或下划线。 C++ 区分大小写。

关键词

C++ 有一些具有特殊含义的关键字,不能用作标识符。 例如:int、if、else、for、while等。

数据类型

C++有基本数据类型和用户定义数据类型。 基本数据类型包括整型、浮点型、字符型、布尔型等。用户定义的数据类型包括结构体、枚举、类等。

多变的

变量是用于存储数据的内存位置。 定义变量时,需要指定变量的数据类型和名称。 变量的值可以在程序运行时修改。

持续的

常量是无法修改的值。 C++ 有文字常量和符号常量。 文字常量是指在代码中直接使用的常量值,例如:10、3.14、'A'等。符号常量是指用#或const关键字定义的常量,例如:#PI 3.14, const int = 100等

操作员

C++支持算术运算符、关系运算符、逻辑运算符、位运算符等。例如:+、-、*、/、%、==、!=、、&&、||、&、|等。

控制语句

C++有两种控制语句:选择结构和循环结构。 选择结构包括if语句和语句,循环结构包括while语句、do-while语句和for语句。 这些控制语句可以根据条件执行不同的代码块,从而控制程序的执行流程。

功能

预处理/编译/汇编/链接

预处理:预定义宏编译:编译器需要做的是通过词法分析和语法分析,确认所有指令符合语法规则后,将其翻译为等效的中间代码表示或汇编代码。 汇编:将汇编语言代码翻译成目标机器指令的过程链接:链接器的主要工作是将相关的目标文件相互链接起来,即将一个文件中引用的符号与另一个文件,使所有这些目标文件成为一个统一的整体,可以被操作系统加载和执行。

内存分区

C++的内存分区主要包括以下几个部分:

栈区(Stack):由编译器自动分配和释放,存放函数参数值、局部变量值等,其特点是先进后出。

堆区(Heap):一般由程序员分配和释放。 如果程序员不释放它,可能会在程序结束时被操作系统回收。 其特点是先进先出。

全局区():存放全局变量和静态变量,包括已初始化的和未初始化的,程序结束后由操作系统回收。

常量区(Const):存放常量,如字符串常量等,程序结束后会被操作系统回收。

代码区(Code):存放程序的代码,由操作系统分配和回收,不允许写入。

在程序运行过程中,栈区、堆区和全局区的大小可以动态改变,而常量区和代码区的大小是固定的。 程序员在使用内存时,需要注意内存的分配和释放,避免出现内存泄漏、内存溢出等问题。

栈区:函数中的局部变量,如:

void func() {
  int num = 10// num 是一个局部变量,存储在栈区
  // ...
}

堆区:动态分配的内存,如:

int* ptr = new int// 动态分配一个 int 类型的内存,存储在堆区

全局区:全局变量和静态变量,如:

int global_num = 100// 全局变量,存储在全局区
static int static_num = 200// 静态变量,存储在全局区

常量区:常量字符串,如:

const char* str = "Hello, world!"// 常量字符串,存储在常量区

代码区:程序的代码,如:

int main() {
  // 程序的代码,存储在代码区
  return 0;
}

指针和参考

在C++中,引用和指针都可以用来间接访问变量,但它们之间有以下区别:

引用必须在定义时初始化,而指针可以先定义然后赋值。

引用不允许指向 null,而指针可以。

使用时不需要取消引用引用,而需要使用星号 (*) 运算符取消引用指针。

下面是一个演示引用和指针用法的示例:

#include 
using namespace std;

int main() {
 int a = 10;
 int& ref = a; // 定义引用
 int* ptr = &a; // 定义指针

 cout << "a = " << a << endl;
 cout << "ref = " << ref << endl;
 cout << "*ptr = " << *ptr << endl;

 ref = 20// 修改引用的值
 (*ptr) = 30// 修改指针所指向的值

 cout << "a = " << a << endl;
 cout << "ref = " << ref << endl;
 cout << "*ptr = " << *ptr << endl;

 int b = 40;
 ptr = &b; // 指针可以重新指向另一个变量
 // ref = b; // 引用一旦指向变量后不能再指向其他变量

 cout << "b = " << b << endl;
 cout << "*ptr = " << *ptr << endl;

 return 0;
}

输出结果:

a = 10
ref = 10
*ptr = 10
a = 30
ref = 30
*ptr = 30
b = 40
*ptr = 40

在函数参数中,引用和指针都可以用来传递参数,但它们之间有一些区别:

当引用作为函数参数时,传递的是实参的别名,函数内部引用的修改将直接影响实参的值。 当指针作为函数参数时,传递的是实参的地址,需要使用解引用运算符(*)来访问实参的值。 函数内部对指针的修改不会影响实参的地址,但是可以通过指针修改一个实参的值。

当引用作为函数参数时,不需要使用地址符号(&)来传递参数,而当指针作为函数参数时,需要使用地址符号(&)来传递参数。

当引用作为函数参数时,不能为空,而当指针作为函数参数时,可以为空。

下面是一个示例,演示了如何使用引用和指针作为函数参数:

#include 
using namespace std;

void changeByRef(int& x) {
 x = 20;
}

void changeByPtr(int* x) {
 (*x) = 30;
}

int main() {
 int a = 10;

 changeByRef(a);
 cout << "a = " << a << endl;

 changeByPtr(&a);
 cout << "a = " << a << endl;

 int* ptr = nullptr;
 // changeByRef(ptr); // 引用不能为null
 changeByPtr(ptr); // 指针可以为null

 return 0;
}

输出结果:

a = 20
a = 30

可见,通过引用和指针都可以修改实参的值,但引用更加简洁,不需要使用解引用运算符(*),并且可以避免空指针的问题。 但指针可以指向 null,这有时很有用。

C++构造函数和析构函数(这部分看似简单其实细节很多)

C++中的构造函数和析构函数是特殊的成员函数,它们在创建和销毁对象时执行特定的操作。 构造函数用于初始化对象的成员变量,析构函数用于释放对象占用的资源。

这是示例代码:

#include 

class MyClass {
public:
    MyClass() {
        std::cout << "Constructor called" << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructor called" << std::endl;
    }
};

int main() {
    MyClass obj; // 创建对象时调用构造函数
    return 0// 程序结束时调用析构函数
}

防范措施:

构造函数和析构函数与类具有相同的名称,并且没有返回类型。

构造函数可以有参数来初始化成员变量,也可以没有参数。

如果没有定义构造函数,编译器会生成一个默认构造函数。

如果类定义了析构函数,编译器将不会生成默认析构函数。

构造函数和析构函数的访问可以是 , 或 。

构造函数和析构函数不能被继承。

在构造函数中,可以使用初始化列表来初始化成员变量,这比构造函数体内赋值的效率更高。

在析构函数中,应该释放对象所占用的资源,如堆内存、文件句柄等。

C++ 面向对象

C++的三大特点是:

封装

封装是数据和操作数据的方法(函数)的组合,形成一个类。 类将数据和方法封装在一起,使得外部无法直接访问和修改数据,而只能通过类的公共接口来访问和修改。 这样可以保证数据的安全性和可靠性,同时也提高代码的可维护性和可扩展性。

下面通过一个例子来说明封装的概念。 假设我们要写一个银行账户管理系统,每个账户都包含账户名、账号、账户余额等信息。 我们可以定义一个类来封装这些信息,具体实现如下:

class Account {
private// 私有成员变量,外部代码无法访问和修改
    string name; // 账户名称
    string number; // 账户号码
    double balance; // 账户余额

public// 公共成员函数,外部代码可以调用
    Account(string n, string num, double bal) { // 构造函数
        name = n;
        number = num;
        balance = bal;
    }

    string getName() // 获取账户名称
        return name;
    }

    string getNumber() // 获取账户号码
        return number;
    }

    double getBalance() // 获取账户余额
        return balance;
    }

    void deposit(double amount) // 存款操作
        balance += amount;
    }

    void withdraw(double amount) // 取款操作
        if (balance < amount) {
            cout << "余额不足!" << endl;
        } else {
            balance -= amount;
        }
    }
};

在上面的代码中,我们将数据成员(name,,)声明为私有,外部代码无法直接访问和修改这些数据。 同时,我们提供公共成员函数(、、、、)来访问和修改这些数据。 这样外部代码只能通过公共接口来操作数据,从而保证了数据的安全可靠。

例如,我们可以通过以下代码创建一个账户对象并进行存取款操作:

Account myAccount("John Smith""1234567890"1000.0);
myAccount.deposit(500.0);
myAccount.withdraw(2000.0);

通过封装,我们可以将数据和操作数据的方法封装在一起,提高了代码的可维护性和可扩展性,同时也保证了数据的安全性和可靠性。

继承

继承意味着一个类可以继承另一个类的属性和方法。 继承的类称为基类或父类,继承的类称为派生类或子类。 子类可以重写父类的方法,也可以添加新的属性和方法。 继承可以提高代码的重用性和扩展性,同时还可以实现多态性。

C++继承是面向对象编程中的一个重要概念,它允许一个类继承另一个类的属性和方法。 C++ 支持单继承和多重继承。

单继承:派生类只能继承一个基类的属性和方法。 语法格式如下:

class 派生类名 : 继承方式 基类名 {
    // 派生类的成员和方法
};

继承方式可以是,或者,分别表示公有继承、受保护继承和私有继承。

这是一个简单的单继承示例:

class Animal {
public:
    string name;
    void eat() {
        cout << name << " is eating..." << endl;
    }
};

class Cat : public Animal {
public:
    void meow() {
        cout << name << " is meowing..." << endl;
    }
};

int main() {
    Cat cat;
    cat.name = "Tom";
    cat.eat();  // 继承自父类的方法
    cat.meow(); // 子类自己的方法
    return 0;
}

在此示例中,Cat 是基类,Cat 是派生类。 Cat继承了属性和方法,并添加了自己的方法meow。 创建Cat的实例后,可以调用父类的方法eat和子类本身的方法meow。

多重继承:派生类可以从多个基类继承属性和方法。 语法格式如下:

class 派生类名 : 继承方式 基类名1, 继承方式 基类名2, ... {
    // 派生类的成员和方法
};

这是一个简单的多重继承示例:

class Animal {
public:
    string name;
    void eat() {
        cout << name << " is eating..." << endl;
    }
};

class Flyable {
public:
    void fly() {
        cout << "I am flying..." << endl;
    }
};

class Bird : public Animal, public Flyable {
public:
    void chirp() {
        cout << name << " is chirping..." << endl;
    }
};

int main() {
    Bird bird;
    bird.name = "Sparrow";
    bird.eat();  // 继承自Animal的方法
    bird.fly();  // 继承自Flyable的方法
    bird.chirp(); // 子类自己的方法
    return 0;
}

在这个例子中,Bird继承了两个基类的属性和方法,并添加了自己的方法chirp。 创建Bird实例后,可以调用父类的eat和fly方法以及子类的chirp方法。

C++继承还涉及到虚函数、重载、多态性等高级特性,可以更好地组织和管理复杂的程序。

多态性

多态性意味着同一类型的对象在不同情况下可以有不同的行为。 C++中实现多态性的方法有两种:虚函数和模板。 虚函数是指在基类中定义了一个虚函数,可以在派生类中进行重写,当通过基类指针或引用调用该函数时,会根据所指向的对象类型来调用相应的函数。 模板是指可以接受不同类型参数的通用函数或类的定义。 根据参数类型的不同,会生成不同的函数或者类,实现多态。 多态性可以提高代码的灵活性和可扩展性。

C++多态性是面向对象编程中的一个重要概念,它允许不同的对象对同一消息做出不同的响应。 多态性可以提高代码的可维护性和可扩展性,使程序更加灵活。

C++多态实现的基础是虚函数( ),它是在基类中声明的函数,并且可以在派生类中重写( )。 通过在基类中将函数声明为虚函数,派生类中的同名函数可以自动成为虚函数,并且可以动态绑定()。

这是一个简单的多态示例:

class Animal {
public:
    virtual void speak() {
        cout << "The animal is speaking..." << endl;
    }
};

class Cat : public Animal {
public:
    void speak() {
        cout << "The cat is meowing..." << endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        cout << "The dog is barking..." << endl;
    }
};

int main() {
    Animal* animal1 = new Cat();
    Animal* animal2 = new Dog();
    animal1->speak(); // 多态调用Cat的speak方法
    animal2->speak(); // 多态调用Dog的speak方法
    return 0;
}

在本例中, 是基类,Cat 和 Dog 是派生类。 中的speak函数被声明为虚函数,Cat和Dog重写了speak函数。 创建的指针指向Cat和Dog的实例后,调用speak函数会根据实际指向的对象进行动态绑定,从而实现多态。

C++多态还可以通过抽象类(Class)和纯虚函数(Pure)来实现。 抽象类是无法实例化的类。 它至少包含一个纯虚函数。 纯虚函数是未实现的虚函数。 派生类必须重写要实例化的纯虚函数。 抽象类和纯虚函数可以强制派生类必须实现的接口,使程序更加健壮和可靠。

C++ 中的模板是一种通用编程工具,允许我们为不同类型的数据编写可重用的代码。 模板可以定义类模板和函数模板,其中类模板用于定义公共数据类型,函数模板用于定义公共函数。

类模板示例:

template 
class myVector {
private:
    T* data;
    int size;
public:
    myVector(int n) {
        data = new T[n];
        size = n;
    }
    T& operator[](int index) {
        return data[index];
    }
};

在此示例中,我们定义了一个名为 的类模板,它采用类型 T 作为参数。 这个类有一个私有数据成员data,它是一个指向类型T的指针,还有一个size变量,它用来存储向量的大小。 该类还定义了一个构造函数和一个用于访问向量中元素的运算符[]。

函数模板示例:

template
T maximum(T x, T y) {
    return (x > y) ? x : y;
}

在此示例中,我们定义了一个名为 的函数模板,它采用类型 T 作为参数。 该函数接受两个参数 x 和 y,它们的类型必须相同。 该函数比较x和y的值并返回较大的值。

使用模板的示例:

myVector v(5);
v[0] = 1;
v[1] = 2;
v[2] = 3;
v[3] = 4;
v[4] = 5;
cout << maximum(3, 5) << endl;
cout << maximum(3.5, 2.8) << endl;
cout << maximum('a''b') << endl;

在这个例子中,我们首先创建一个存储 5 个整数的对象 v。 然后我们使用函数模板来比较不同类型的值,例如整数、浮点数和字符。 这些值都是模板参数T的实例化。

C++11的特点

C++11是C++语言的一个重要版本,引入了许多新特性,包括语言特性、标准库特性等等。 以下是 C++11 的一些新功能:

自动类型推导(auto关键字)

C++11引入了auto关键字,它可以自动推导变量的类型:

auto i = 42// i的类型为int
auto d = 3.14// d的类型为double

基于范围的 for 循环(基于范围的 for 循环)

C++11引入了基于范围的for循环,可以轻松遍历容器中的元素:

std::vector<int> v = {123};
for (auto i : v) {
    std::cout << i << std::endl;
}

关键词

C++11引入了可以替换NULL指针常量的关键字:

int* p = nullptr// p指向空指针

右值引用 ( )

C++11引入了右值引用,它可以绑定到右值表达式:

int&& r = 1// r绑定到右值1上

右值引用可用于实现移动语义和完美转发。

移动语义(move)

C++11引入了移动语义,它可以将资源所有权从一个对象转移到另一个对象,避免不必要的复制操作:

std::vector<int> v1 = {123};
std::vector<int> v2 = std::move(v1); // 转移v1的资源所有权到v2

表达

C++11引入了表达式,可以方便地定义匿名函数:

auto f = [](int x, int y) { return x + y; }; // 定义lambda表达式
int z = f(12); // 调用lambda表达式

关键词

C++11 引入了可以在编译时求值的关键字:

constexpr int fac(int n) {
    return n == 0 ? 1 : n * fac(n - 1);
}
int x = fac(5); // 在编译时求出5的阶乘

智能指针(智能)

C++11引入了智能指针,可以自动管理资源的生命周期,避免内存泄漏:

std::unique_ptr<intp(new int(42))// 定义一个独占指针
std::shared_ptr<intq(new int(42))// 定义一个共享指针

打回来

#include 

void callback() {

 std::cout << "回调函数的执行!"<<std::endl;
}

void dodo(void (*callback)()) {

 std::cout << "调用回调函数的函数"<<std::endl;
 callback();
}

int main() {

 dodo(callback);
 return 0;
}

多线程和共享内存&互斥互斥

#include
#include   //时间库
#include 
#include
#include  //匿名函数库
#include
using namespace std;


class SyncQueue {

public:
 // 初始化队列中的数量
 SyncQueue(int maxSize) :m_maxSize(maxSize) {}

 // 放入
 void put(const int& x) {
  // 添加循环
  while (1) {
   //条件变量只能与unique_lock一起使用
   unique_lock locker(m_mutex);
   m_notFull.wait(locker, [this]() {
    return m_queue.size() != m_maxSize;
    });
   m_queue.push_back(x);
   cout << "生产者产出:" << x << endl;
   // 加上延迟时间
   this_thread::sleep_for(std::chrono::seconds(1));
   m_notEmpty.notify_one();
  }
 }

 //拿出
 void take() {
  // 添加循环
  while (1) {
   //条件变量只能与unique_lock一起使用
   unique_lock locker(m_mutex);
   m_notEmpty.wait(locker, [this]() {
    return !m_queue.empty(); });
   int x = m_queue.front();
   m_queue.pop_front();
   cout << "消费者消费:" << x << endl;
   // 加上延迟时间
   this_thread::sleep_for(std::chrono::seconds(1));
   m_notFull.notify_one();
  }
 }

private:
 list<int> m_queue;  //存储队列数据
 mutex m_mutex;  //互斥锁
 condition_variable m_notEmpty;  //不为空的条件变量
 condition_variable m_notFull;   //不为满的条件变量

 int m_maxSize;  //任务队列的最大任务个数
};

int main() {

 SyncQueue Tasks(5);

 //绑定生成函数
 auto producer = bind(&SyncQueue::put, &Tasks, placeholders::_1);
 auto consume = bind(&SyncQueue::take, &Tasks);

 thread t1[3];
 thread t2[3];

 for (int i = 0; i < 3; ++i) {
  t1[i] = thread(producer, i + 100);
  t2[i] = thread(consume);
 }

 for (int i = 0; i < 3; ++i) {
  t1[i].join();
  t2[i].join();
 }

 return 0;
}

STL标准模板库

STL()是C++标准库的重要组成部分,它提供了一组通用模板类和函数,用于实现各种常用的数据结构和算法,如容器、迭代器、算法和函数对象等,使得C++程序员可以更轻松地编程。

STL库的主要组成部分包括以下几个方面:

():STL提供了多种容器,例如,list、deque、set、map等,用于存储和管理不同类型的数据。 这些容器实现了各种数据结构,如数组、链表、树等,并提供丰富的接口和算法,方便快捷的数据操作。

():STL提供了多种迭代器,如输入迭代器、输出迭代器、正向迭代器、双向迭代器、随机访问迭代器等,用于遍历容器中的元素。 迭代器提供了统一的接口,可以实现算法和容器之间的无缝协作。

算法():STL提供了大量的算法,如排序、搜索、复制、变换等,用于处理容器中的元素。 这些算法实现了各种常用的操作,如求最大值、求和、去重等,可以大大提高程序的效率和可读性。

函数对象():STL提供了多种函数对象,例如一元函数对象、二元函数对象、谓词等,来对容器中的元素进行操作。 函数对象为程序员提供了一种方便的方式来定制算法和操作,以更好地满足不同的需求。

使用STL库可以大大提高C++程序的效率和可读性,也为程序员提供更多的便利。 在实际编程中,程序员可以根据不同的需求选择合适的容器、迭代器、算法和函数对象,从而实现高效的数据操作和算法实现。

单例模式

饿汉模式:

单例对象是在程序启动时创建的,因此也称为“全人模式”。 线程安全性较好,但可能会浪费资源。

class Singleton {
private:
    Singleton() {}
    static Singleton* instance;
public:
    static Singleton* getInstance() {
        return instance;
    }
};

Singleton* Singleton::instance = new Singleton();

懒惰模式:

仅在需要使用时才创建单例对象,因此也称为“惰性模式”。 需要考虑线程安全问题,否则可能会导致多个线程同时创建单例对象。

class Singleton {
private:
    Singleton() {}
    static Singleton* instance;
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;

线程安全问题可以通过加锁来实现,例如使用std::mutex:

class Singleton {
private:
    Singleton() {}
    static Singleton* instance;
    static std::mutex mtx;
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {
                instance = new Singleton();
            }
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

数据结构及排序算法(推荐大话数据结构) C++中常用的数据结构如下:

数组(Array):一组连续的内存单元,用于存储相同类型的数据。

链表(List):一组由指针连接的不连续的内存单元,可以动态添加或删除元素。

栈:一种后进先出(LIFO)数据结构,只能在栈顶插入和删除。

队列:先进先出(FIFO)的数据结构,只能在队尾插入,在队头删除。

树:由节点和边组成的非线性数据结构,每个节点可以有多个子节点。

图:由节点和边组成的非线性数据结构,每个节点可以有多个相邻节点。

堆:一种特殊的树形数据结构,常用于实现优先级队列。

哈希表:通过哈希函数将键映射到值的数据结构,从而实现高效的查找和插入操作。

直接插入排序的原理?

稳定,平均/最差时间复杂度为O(n²),元素基本有序时最佳时间复杂度为O(n),空间复杂度为O(1)。

在每一遍中,将一条待排序记录插入到已根据其键的大小排序的一组记录的适当位置,直到插入所有待排序记录。

public void insertionSort(int[] nums) {
    for (int i = 1; i < nums.length; i++) {
        int insertNum = nums[i];
        int insertIndex;
        for (insertIndex = i - 1; insertIndex >= 0 && nums[insertIndex] > insertNum; insertIndex--) {
            nums[insertIndex + 1] = nums[insertIndex];
        }
        nums[insertIndex + 1] = insertNum;
    }
}COPY

直接插入没有利用要插入的序列已经排序的事实。 插入第i个元素时,可以通过二分查找找到插入位置,然后将i~之间的所有元素移动一位,将第i个元素放到插入位置。

public void binaryInsertionSort(int[] nums) {
for (int i = 1; i < nums.length; i++) {
int insertNum = nums[i];
int insertIndex = -1;
int start = 0;
int end = i - 1;
while (start <= end) {
int mid = start + (end - start) / 2;
if (insertNum > nums[mid])
start = mid + 1;
else if (insertNum < nums[mid])
end = mid - 1;
else {
insertIndex = mid + 1;
break;
}
}
if (insertIndex == -1)
insertIndex = start;
if (i - insertIndex >= 0)
System.arraycopy(nums, insertIndex, nums, insertIndex + 1, i - insertIndex);
nums[insertIndex] = insertNum;
}
}

希尔排序的原理?

又称收缩增量排序,是直接插入排序的改进,不稳定,平均时间复杂度O(n^1.3^),最差时间复杂度O(n²),最佳时间复杂度O(n),空间复杂度O(1 )。

将记录按一定的下标增量进行分组,每组直接插入排序。 每次排序后,增量都会减少。 当增量减至1时,排序完成。

public void shellSort(int[] nums) {
for (int d = nums.length / 2; d > 0 ; d /= 2) {
for (int i = d; i < nums.length; i++) {
int insertNum = nums[i];
int insertIndex;
for (insertIndex = i - d; insertIndex >= 0 && nums[insertIndex] > insertNum; insertIndex -= d) {
nums[insertIndex + d] = nums[insertIndex];
}
nums[insertIndex + d] = insertNum;
}
}
}

直接选择排序如何工作?

不稳定,时间复杂度O(n²),空间复杂度O(1)。

每次在未排序序列中找到最小元素时,与未排序序列的第一个元素交换位置,然后在剩余的未排序序列中重复该操作,直到所有元素都已排序。

public void selectSort(int[] nums) {
int minIndex;
for (int index = 0; index < nums.length - 1; index++){
minIndex = index;
for (int i = index + 1;i < nums.length; i++){
if(nums[i] < nums[minIndex])
minIndex = i;
}
if (index != minIndex){
swap(nums, index, minIndex);
}
}
}

堆排序的原理?

是直接选择排序的改进,不稳定,时间复杂度O(nlogn),空间复杂度O(1)。

将待排序的记录视为完全二叉树,可以建立大根堆或小根堆。 大根堆中每个节点的值不小于其子节点的值,小根堆中每个节点的值不大于其子​​节点的值。 节点值。

以大根堆为例,构建堆时,首先将最后一个节点作为当前节点。 如果当前节点有父节点并且该值大于父节点,则交换当前节点和父节点。 移除时,先暂时存储根节点的值,然后将根节点替换为最后一个节点作为当前节点。 如果当前节点有子节点且值小于子节点,则与值较大的子节点进行交换,并调整堆完毕后返回临时值。

public void add(int[] nums, int i, int num){
nums[i] = num;
int curIndex = i;
while (curIndex > 0) {
int parentIndex = (curIndex - 1) / 2;
if (nums[parentIndex] < nums[curIndex])
swap(nums, parentIndex, curIndex);
else break;
curIndex = parentIndex;
}
}

public int remove(int[] nums, int size){
int result = nums[0];
nums[0] = nums[size - 1];
int curIndex = 0;
while (true) {
int leftIndex = curIndex * 2 + 1;
int rightIndex = curIndex * 2 + 2;
if (leftIndex >= size) break;
int maxIndex = leftIndex;
if (rightIndex < size && nums[maxIndex] < nums[rightIndex])
maxIndex = rightIndex;
if (nums[curIndex] < nums[maxIndex])
swap(nums, curIndex, maxIndex);
else break;
curIndex = maxIndex;
}
return result;
}

冒泡排序的原理?

稳定,平均/最差时间复杂度为O(n²),元素基本有序时最佳时间复杂度为O(n),空间复杂度为O(1)。

比较相邻元素,如果第一个大于第二个则交换,对每对相邻元素都做同样的操作,从开始的第一对到结束的最后一对,每轮排序后的最后一个元素是全部按顺序排列,对n个元素重复上述步骤n -1次即可完成排序。

public void bubbleSort(int[] nums) {
for (int i = 0; i < nums.length - 1; i++) {
for (int index = 0; index < nums.length - 1 - i; index++) {
if (nums[index] > nums[index + 1])
swap(nums, index, index + 1)
}
}
}

当序列已经排序时,仍然会进行不必要的比较。 可以设置一个标志来记录是否有元素交换,如果没有则直接结束比较。

public void betterBubbleSort(int[] nums) {
boolean swap;
for (int i = 0; i < nums.length - 1; i++) {
swap = true;
for (int index = 0; index < nums.length - 1 - i; index++) {
if (nums[index] > nums[index + 1]) {
swap(nums, index ,index + 1);
swap = false;
}
}
if (swap) break;
}
}

快速排序的原理?

是对冒泡排序的改进,不稳定,平均/最佳时间复杂度为O(nlogn),元素基本有序时最差时间复杂度为O(n²),空间复杂度为O(logn)。

首先选择一个参考元素,通过一次排序将待排序数据分成两个独立的部分,一部分全部小于等于参考元素,一部分全部大于等于参考元素,然后按照这个方法对两部分数据进行递归排序。

快速排序的一划分从两端交替搜索,直到低指针和高指针重合,一趟的时间复杂度为O(n)。 整个算法的时间复杂度与划分数有关。

在最好的情况下,每次划分选择的中间数正好将当前序列均分,经过log(n)次划分后,可以得到长度为1的子表,因此时间复杂度为O(nlogn)。

最坏的情况是每次选择的中间数是当前序列中最大或最小的元素,这使得每次划分得到的子表之一成为空表,所以长度为n的数据表需要n次的除法,整个排序时间复杂度为O(n²)。

public void quickSort(int[] nums, int start, int end) {
if (start < end) {
int pivotIndex = getPivotIndex(nums, start, end);
quickSort(nums, start, pivotIndex - 1);
quickSort(nums, pivotIndex + 1, end);
}
}

public int getPivotIndex(int[] nums, int start, int end) {
int pivot = nums[start];
int low = start;
int high = end;
while (low < high) {
while (low <= high && nums[low] <= pivot)
low++;
while (low <= high && nums[high] > pivot)
high--;
if (low < high)
swap(nums, low, high);
}
swap(nums, start, high);
return high;
}

归并排序的原理?

归并排序基于合并操作,是一种稳定的排序算法。 无论如何,时间复杂度是O(nlogn),空间复杂度是O(n)。

基本原理:应用分而治之的方法,将待排序的序列分为两部分,然后对两部分进行递归排序,最后合并,使用辅助空间并设置两个指针指向两个已排序的起始元素分别为序列。 将指针对应的较小元素添加到辅助空间,重复此步骤,直到一个序列到达末尾,然后将另一个序列的剩余元素合并到辅助空间的末尾。

适用场景:数据量较大、需要稳定性的场合。

int[] help;

public void mergeSort(int[] arr) {
int[] help = new int[arr.length];
sort(arr, 0, arr.length - 1);
}

public void sort(int[] arr, int start, int end) {
if (start == end) return;
int mid = start + (end - start) / 2;
sort(arr, start, mid);
sort(arr, mid + 1, end);
merge(arr, start, mid, end);
}

public void merge(int[] arr, int start, int mid, int end) {
if (end + 1 - start >= 0) System.arraycopy(arr, start, help, start, end + 1 - start);
int p = start;
int q = mid + 1;
int index = start;
while (p <= mid && q <= end) {
if (help[p] < help[q])
arr[index++] = help[p++];
else
arr[index++] = help[q++];
}
while (p <= mid) arr[index++] = help[p++];
while (q <= end) arr[index++] = help[q++];
}

如何选择排序算法?

数据量较小,考虑直接插入或直接选择。 当元素有序分布时,直接插入会大大减少比较和移动记录的次数。 如果对稳定性要求不高,可以采用直接选择,效率比直接插入稍高。

如果数据量中等,选择希尔排序。

数据量大,考虑堆排序(元素分布接近正序或倒序)、快速排序(元素分布随机)和归并排序(稳定)。

TCP/UDP(B站计算机网络微课堂入门推荐)

TCP套接字是一种可靠的网络通信协议,可以保证数据的完整性、有序性和可靠性。 用C++实现TCP套接字的客户端与服务器端的通信,主要分为以下步骤:

创建:使用()函数创建对象,指定协议类型和通信方式。

绑定地址:服务器需要使用bind()函数将对象绑定到本地地址,但客户端不需要。

监听连接:服务端需要使用()函数开始监听客户端的连接请求,但客户端不需要。

建立连接:客户端使用()函数向服务器发起连接请求,服务器使用()函数接受客户端的连接请求。

通信:客户端和服务器通过send()和recv()函数发送和接收数据。

关闭连接:客户端和服务器端使用close()函数关闭连接。

下面是一个简单的TCP通信的例子,其中包括客户端和服务器代码:

服务器代码:

#include 
#include 
#include 
#include 
#include 

using namespace std;

int main() {
    // 创建Socket
    int serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverSocket == -1) {
        cerr << "Failed to create socket" << endl;
        return -1;
    }

    // 绑定地址
    sockaddr_in serverAddr;
    memset(&serverAddr, 0sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(8080);
    if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
        cerr << "Failed to bind address" << endl;
        close(serverSocket);
        return -1;
    }

    // 监听连接
    if (listen(serverSocket, 10) == -1) {
        cerr << "Failed to listen" << endl;
        close(serverSocket);
        return -1;
    }

    // 等待连接请求
    sockaddr_in clientAddr;
    socklen_t clientAddrLen = sizeof(clientAddr);
    int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrLen);
    if (clientSocket == -1) {
        cerr << "Failed to accept connection" << endl;
        close(serverSocket);
        return -1;
    }

    // 接收客户端发送的消息
    char buffer[1024];
    memset(buffer, 0sizeof(buffer));
    if (recv(clientSocket, buffer, sizeof(buffer), 0) == -1) {
        cerr << "Failed to receive data" << endl;
        close(clientSocket);
        close(serverSocket);
        return -1;
    }
    cout << "Received message from client: " << buffer << endl;

    // 发送消息给客户端
    const char* message = "Hello, client!";
    if (send(clientSocket, message, strlen(message), 0) == -1) {
        cerr << "Failed to send data" << endl;
        close(clientSocket);
        close(serverSocket);
        return -1;
    }

    // 关闭连接
    close(clientSocket);
    close(serverSocket);

    return 0;
}

客户端代码:

#include 
#include 
#include 
#include 
#include 

using namespace std;

int main() {
    // 创建Socket
    int clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == -1) {
        cerr << "Failed to create socket" << endl;
        return -1;
    }

    // 连接服务器
    sockaddr_in serverAddr;
    memset(&serverAddr, 0sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serverAddr.sin_port = htons(8080);
    if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
        cerr << "Failed to connect to server" << endl;
        close(clientSocket);
        return -1;
    }

    // 发送消息给服务器
    const char* message = "Hello, server!";
    if (send(clientSocket, message, strlen(message), 0) == -1) {
        cerr << "Failed to send data" << endl;
        close(clientSocket);
        return -1;
    }

    // 接收服务器发送的消息
    char buffer[1024];
    memset(buffer, 0sizeof(buffer));
    if (recv(clientSocket, buffer, sizeof(buffer), 0) == -1) {
        cerr << "Failed to receive data" << endl;
        close(clientSocket);
        return -1;
    }
    cout << "Received message from server: " << buffer << endl;

    // 关闭连接
    close(clientSocket);

    return 0;
}

上面的例子中,服务器监听本地地址端口8080的连接请求,收到客户端的连接请求后,接收客户端发送的消息并发送回复消息。 客户端连接本地地址127.0.0.1的8080端口,向服务器发送消息,然后接收服务器发送的回复消息。 最后,客户端和服务器都关闭连接。

UDP套接字是一种无连接的网络通信协议,不保证数据的可靠性和顺序,但延迟低、传输速率高。 在C++中,UDP套接字的客户端与服务器端的通信主要分为以下步骤:

创建:使用()函数创建对象,指定协议类型和通讯方式。

绑定地址:服务器和客户端都可以使用bind()函数将对象绑定到本地地址。

通信:客户端和服务器通过()和()函数发送和接收数据。

关闭:客户端和服务器端使用close()函数来关闭。

下面是一个简单的UDP通信的例子,其中包括客户端和服务器代码:

服务器代码:

#include 
#include 
#include 
#include 
#include 

using namespace std;

int main() {
    // 创建Socket
    int serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (serverSocket == -1) {
        cerr << "Failed to create socket" << endl;
        return -1;
    }

    // 绑定地址
    sockaddr_in serverAddr;
    memset(&serverAddr, 0sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(8080);
    if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
        cerr << "Failed to bind address" << endl;
        close(serverSocket);
        return -1;
    }

    // 接收客户端发送的消息
    char buffer[1024];
    memset(buffer, 0sizeof(buffer));
    sockaddr_in clientAddr;
    socklen_t clientAddrLen = sizeof(clientAddr);
    if (recvfrom(serverSocket, buffer, sizeof(buffer), 0, (sockaddr*)&clientAddr, &clientAddrLen) == -1) {
        cerr << "Failed to receive data" << endl;
        close(serverSocket);
        return -1;
    }
    cout << "Received message from client: " << buffer << endl;

    // 发送消息给客户端
    const char* message = "Hello, client!";
    if (sendto(serverSocket, message, strlen(message), 0, (sockaddr*)&clientAddr, clientAddrLen) == -1) {
        cerr << "Failed to send data" << endl;
        close(serverSocket);
        return -1;
    }

    // 关闭Socket
    close(serverSocket);

    return 0;
}

客户端代码:

#include 
#include 
#include 
#include 
#include 

using namespace std;

int main() {
    // 创建Socket
    int clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (clientSocket == -1) {
        cerr << "Failed to create socket" << endl;
        return -1;
    }

    // 发送消息给服务器
    const char* message = "Hello, server!";
    sockaddr_in serverAddr;
    memset(&serverAddr, 0sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serverAddr.sin_port = htons(8080);
    if (sendto(clientSocket, message, strlen(message), 0, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
        cerr << "Failed to send data" << endl;
        close(clientSocket);
        return -1;
    }

    // 接收服务器发送的消息
    char buffer[1024];
    memset(buffer, 0sizeof(buffer));
    sockaddr_in serverResponseAddr;
    socklen_t serverResponseAddrLen = sizeof(serverResponseAddr);
    if (recvfrom(clientSocket, buffer, sizeof(buffer), 0, (sockaddr*)&serverResponseAddr, &serverResponseAddrLen) == -1) {
        cerr << "Failed to receive data" << endl;
        close(clientSocket);
        return -1;
    }
    cout << "Received message from server: " << buffer << endl;

    // 关闭Socket
    close(clientSocket);

    return 0;
}

上面的例子中,服务器监听本地地址端口8080的UDP连接请求,收到客户端的连接请求后,接收客户端发送的消息并发送回复消息。 客户端连接本地地址127.0.0.1的8080端口,向服务器发送消息,然后接收服务器发送的回复消息。 最后,客户端和服务器都关闭。

数据库(MYSQL)

连接数据库:mysql -u -p -h

显示数据库列表: show ;

创建数据库:;

选择数据库:使用 ;

显示数据表: show ;

创建数据表:table( , , ...);

插入数据:到 (, , ...) (, , ...);

查询数据:*来自;

更新数据:set =, = where ;

删除数据:从哪里删除;

the data table: drop table ;

: drop ;

data: .sql;

data: -u -p > .sql

The is a , we have a table () and a grade table (), their is as :

table():

约翰

20

爱丽丝

22

彼得

19

汤姆

21

20

():

数学

80

85

数学

90

95

数学

75

80

数学

85

90

数学

95

10

100

Now we can query and this data using the and :

to the : mysql -u root -p -h

list: show ;

: mydb;

: use mydb;

the data table: show ;

a data table:

create table students(
    id int primary key,
    name varchar(50),
    age int
);

create table scores(
    id int primary key,
    student_id int,
    subject varchar(50),
    score int,
    foreign key (student_id) references students(id)
);

插入数据:

insert into students(id, name, age) values(1, 'John', 20);
insert into students(id, name, age) values(2, 'Alice', 22);
insert into students(id, name, age) values(3, 'Peter', 19);
insert into students(id, name, age) values(4, 'Tom', 21);
insert into students(id, name, age) values(5, 'Rachel', 20);

insert into scores(id, student_id, subject, score) values(1, 1, 'Math', 80);
insert into scores(id, student_id, subject, score) values(2, 1, 'English', 85);
insert into scores(id, student_id, subject, score) values(3, 2, 'Math', 90);
insert into scores(id, student_id, subject, score) values(4, 2, 'English', 95);
insert into scores(id, student_id, subject, score) values(5, 3, 'Math', 75);
insert into scores(id, student_id, subject, score) values(6, 3, 'English', 80);
insert into scores(id, student_id, subject, score) values(7, 4, 'Math', 85);
insert into scores(id, student_id, subject, score) values(8, 4, 'English', 90);
insert into scores(id, student_id, subject, score) values(9, 5, 'Math', 95);
insert into scores(id, student_id, subject, score) values(10, 5, 'English', 100);

Query data:

-- 查询所有学生的信息
select * from students;

-- 查询所有学生的姓名和年龄
select name, age from students;

-- 查询所有成绩大于等于90分的学生的姓名和科目
select students.name, scores.subject from students join scores on students.id=scores.student_id where score>=90;

-- 查询每个学生的平均成绩
select students.name, avg(score) from students join scores on students.id=scores.student_id group by students.id;

data:

-- 将John的年龄修改为21
update students set age=21 where name='John';

data:

-- 删除Peter的信息
delete from students where name='Peter';

-- 删除所有成绩表中的数据
delete from scores;

data table:

drop table students;
drop table scores;

:

drop database mydb;

data:

source /path/to/filename.sql;

data:

mysqldump -u root -p password mydb > /path/to/filename.sql;

操作系统

Linux is an open , free whose was by Linus in 1991 and has now one of the most in the world.

常用命令:

Vim is a text used in Linux and Unix . It is an of the Vi , with and a high of . The are some of Vim:

In to these basic , Vim has many other , such as macro , multi- , code , and more. Vim may have a steep curve, but once you its basic , you can the of text .

The is a C++ , with the Vim :

Open a and enter the to start the Vim :

vim hello.cpp

This will a new file "hello.cpp" and open it in Vim.

Enter mode and enter the code:

#include 

int main()
{
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

This "Hello, world!" and then 0.

Save the file and exit Vim. First press the Esc key to to mode, then enter the :

:wq

This will save the file and exit Vim.

the using the GCC . 在终端窗口中输入以下命令:

g++ -o hello hello.cpp

This will an named "hello".

运行程序。 在终端窗口中输入以下命令:

./hello

This will run the and "Hello, world!".

Qt ( Da Bing, who loves at B, and Dalun to speak QT)

Qt is a cross- GUI that a of tools and class so that can high- cross- . The main of Qt the :

Qt core : the basic of Qt, model, and slot , class, file and IO , , etc.

Qt : a of GUI-based , such as , , text boxes, etc., for .

Qt Quick : a QML-based for user , , , and more.

Qt : class to , such as TCP and UDP , HTTP and FTP , etc.

Qt : class for , such as MySQL, , and so on.

Qt : class to audio and video , such as audio and video, audio and video, etc.

Qt and : class for 2D and 3D , such as, etc.

Qt Web : class to Web , such as and .

In to the above , Qt also some other tools and class , such as Qt , Qt Quick for , etc. , Qt is a , , and easy-to-use for types of cross- .

and slot

The and slot is an event in the Qt for . A is a that is when a event , while a slot is a that to a .

In Qt, an can have and slots, and they can be to . When a is , the slot to it will be , thus the .

Using the and slot can make the code more , and . It can help code, code , and and .

Below is a that how to use the and slot to two .

there is a class and a class, when the is , the will a box. code show as below:

// 窗口类
class MyWindow : public QWidget
{
    Q_OBJECT
    
public:
    MyWindow(QWidget *parent = nullptr) : QWidget(parent)
    {
        QPushButton *button = new QPushButton("Click me"this);
        connect(button, SIGNAL(clicked()), this, SLOT(showMessage()));
    }
    
public slots:
    void showMessage()
    
{
        QMessageBox::information(this"Message""Button clicked");
    }
};

// 按钮类
class MyButton : public QPushButton
{
    Q_OBJECT
    
public:
    MyButton(const QString &text, QWidget *parent = nullptr) : QPushButton(text, parent)
    {
        // do something
    }
};

// 主函数
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MyWindow window;
    window.show();
    return app.exec();
}

In the above code, we have a class and a class.在窗口类的构造函数中,我们创建了一个按钮,并将其连接到窗口类的()槽函数上。当按钮被点击时,就会触发()信号,进而自动调用()槽函数,从而显示一个消息框。

这就是一个简单的使用信号和槽机制的例子。通过信号和槽的连接,我们实现了按钮和窗口之间的通信,使程序更加灵活和可扩展。

以下是一个使用Qt5的版本的信号和槽机制的例子:

// 窗口类
class MyWindow : public QWidget
{
    Q_OBJECT
    
public:
    MyWindow(QWidget *parent = nullptr) : QWidget(parent)
    {
        QPushButton *button = new QPushButton("Click me"this);
        connect(button, &QPushButton::clicked, this, &MyWindow::showMessage);
    }
    
public slots:
    void showMessage()
    
{
        QMessageBox::information(this"Message""Button clicked");
    }
};

// 按钮类
class MyButton : public QPushButton
{
    Q_OBJECT
    
public:
    MyButton(const QString &text, QWidget *parent = nullptr) : QPushButton(text, parent)
    {
        // do something
    }
};

// 主函数
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MyWindow window;
    window.show();
    return app.exec();
}

在这个例子中,我们使用了Qt5的新语法来连接信号和槽。具体来说,我们使用了()函数的新语法,将信号和槽通过函数指针连接起来。

在按钮类的构造函数中,我们创建了一个按钮,并将其连接到窗口类的()槽函数上。当按钮被点击时,就会触发()信号,进而自动调用()槽函数,从而显示一个消息框。

这个例子和之前的例子类似,只是使用了Qt5的新语法来连接信号和槽。这种新语法更加简洁明了,使代码更加可读性和可维护性。

Qt的几个窗口

:主窗口,包括菜单栏、工具栏、状态栏等。例如:Qt 的主窗口。

:对话框窗口,用于显示对话框,例如:文件选择对话框、颜色选择对话框等。

:基本窗口,用于显示各种控件,例如:按钮、文本框、标签等。

:浮动窗口,可以被拖拽到主窗口的四周,例如:Qt 的工程浏览器、属性编辑器等。

:启动画面,用于显示程序启动时的欢迎画面。例如:各种桌面应用程序的启动画面。

Qt的常用控件

Qt中常用的控件有很多,下面列举一些常用的控件,并给出简单的例子:

(按钮控件)

QPushButton *button = new QPushButton("Click me"this);
connect(button, &QPushButton::clicked, [=](){
    QMessageBox::information(this"Message""Button clicked");
});

(标签控件)

QLabel *label = new QLabel("Hello World"this);
label->setAlignment(Qt::AlignCenter);

(文本框控件)

QLineEdit *lineEdit = new QLineEdit(this);
lineEdit->setPlaceholderText("Enter your name");
connect(lineEdit, &QLineEdit::returnPressed, [=](){
    QString name = lineEdit->text();
    QMessageBox::information(this"Message""Hello " + name);
});

(多行文本框控件)

QTextEdit *textEdit = new QTextEdit(this);
textEdit->setPlaceholderText("Enter your message");
connect(textEdit, &QTextEdit::textChanged, [=](){
    QString message = textEdit->toPlainText();
    qDebug() << message;
});

(下拉框控件)

QComboBox *comboBox = new QComboBox(this);
comboBox->addItem("Apple");
comboBox->addItem("Banana");
comboBox->addItem("Cherry");
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index){
    QString fruit = comboBox->itemText(index);
    qDebug() << fruit;
});

(数字框控件)

QSpinBox *spinBox = new QSpinBox(this);
spinBox->setRange(0100);
spinBox->setValue(50);
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), [=](int value){
    qDebug() << value;
});

(滑动条控件)

QSlider *slider = new QSlider(Qt::Horizontal, this);
slider->setRange(0100);
slider->setValue(50);
connect(slider, &QSlider::valueChanged, [=](int value){
    qDebug() << value;
});

这些控件只是Qt中的一部分,Qt还提供了很多其他的控件,如、、、等等。这些控件可以帮助我们快速构建各种类型的界面,提高开发效率。

Qt样式表

Qt中的样式表是一种用于定制控件外观的机制,类似于CSS。通过样式表,可以改变控件的颜色、字体、边框等属性,从而实现自定义的外观效果。

以下是一些常用的样式表属性和例子:

-color:设置控件的背景颜色。例如: { -color: red; }

color:设置控件的前景颜色(即文本颜色)。例如: { color: blue; }

font-size:设置控件的字体大小。例如: { font-size: 12px; }

:设置控件的边框。例如: { : 1px solid black; }

:设置控件的内边距(即控件内容与边框之间的距离)。例如: { : 5px; }

:设置控件的外边距(即控件边框与周围控件之间的距离)。例如: { : 10px; }

这些样式表属性可以组合使用,实现更丰富的外观效果。例如,以下样式表将的背景颜色设置为蓝色,边框为圆角,字体为白色,内边距为10px:

{ -color: blue; -: 5px; 白颜色; : 10px; }

最后总结

以上的内容都是简单的罗列,具体的内容还需看书本或者视频,如果是复习可以参考此文章,学习C++不是一蹴而就的,是终身的,基础部分十分的重要,需要反复的学习和体会。

如果你年满18周岁以上,又觉得学【C语言】太难?想尝试其他编程语言,那么我推荐你学Python,现有价值499元Python零基础课程限时免费领取,10名额!


扫描二维码-免费领取

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON