Qt入门之基础篇 ( 二 ) :Qt项目建立、编译、运行和发布过程解析 - 谢维开 - 博客园
c++ for遍历向量举例 ,详细说明
当然,下面是一个使用C++中的for循环来遍历std::vector(向量)的详细例子和说明。
例子:遍历并打印向量中的元素
cppCopy Code
#include <iostream>
#include <vector>
int main() {
// 创建一个整数向量并初始化
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用索引for循环遍历向量
std::cout << "Using index-based for loop:" << std::endl;
for (size_t i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
// 使用范围for循环遍历向量
std::cout << "Using range-based for loop:" << std::endl;
for (int number : numbers) {
std::cout << number << " ";
}
std::cout << std::endl;
return 0;
}
详细说明
- 创建向量:
cppCopy Code
std::vector<int> numbers = {1, 2, 3, 4, 5};
-
- 这行代码创建了一个std::vector<int>类型的向量,名为numbers,并用列表初始化语法给它赋了初值 {1, 2, 3, 4, 5}。
- 索引for循环:
cppCopy Code
for (size_t i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i] << " ";
}
-
- 这个循环使用索引来遍历向量。size_t是一种无符号整数类型,通常用于表示大小和计数。
- numbers.size()返回向量的元素数量。
- 在每次迭代中,i从0开始递增,直到达到向量的末尾。
- numbers[i]访问向量中索引为i的元素,并将其打印出来。
- 范围for循环(C++11及更高版本):
cppCopy Code
for (int number : numbers) {
std::cout << number << " ";
}
-
- 这个循环使用范围for语法来遍历向量。它对向量中的每个元素执行一次循环体,并将元素的值赋给number变量。
- 这种方法更简洁,不需要显式地处理索引。
输出
运行上述代码将产生以下输出:
textCopy Code
Using index-based for loop:
1 2 3 4 5
Using range-based for loop:
1 2 3 4 5
总结
- 使用索引for循环时,你需要手动处理索引和边界条件。
- 使用范围for循环时,循环会自动处理这些细节,使代码更简洁、更易读。
- 在选择使用哪种循环时,要考虑你的具体需求,比如是否需要访问元素的索引或者是否需要修改向量中的元素。如果只需要访问元素的值,范围for循环通常是更好的选择。
C++ 多态函数举例,详细说明
在C++中,多态性(Polymorphism)是面向对象编程中的一个重要特性,它允许你使用统一的接口处理不同类型的对象。多态性通常通过继承和虚函数来实现。下面是一个详细的例子来说明C++中的多态函数。
例子:动物王国的多态性
假设我们有一个基类 Animal,以及两个派生类 Dog 和 Cat。我们希望针对不同类型的动物对象,通过同一个接口来调用它们特有的行为,比如 makeSound()。
1. 定义基类 Animal
cppCopy Code
#include <iostream>
#include <vector>
// 基类
class Animal {
public:
// 虚析构函数,确保派生类的析构函数也能被正确调用
virtual ~Animal() {}
// 纯虚函数,让 Animal 成为抽象类
virtual void makeSound() const = 0;
};
2. 定义派生类 Dog 和 Cat
cppCopy Code
// 派生类 Dog
class Dog : public Animal {
public:
void makeSound() const override {
std::cout << "Woof! Woof!" << std::endl;
}
};
// 派生类 Cat
class Cat : public Animal {
public:
void makeSound() const override {
std::cout << "Meow! Meow!" << std::endl;
}
};
3. 使用多态性
cppCopy Code
int main() {
// 创建一个 Animal 指针的向量,用于存储不同类型的动物对象
std::vector<Animal*> animals;
// 向向量中添加 Dog 和 Cat 对象
animals.push_back(new Dog());
animals.push_back(new Cat());
// 通过遍历向量并调用虚函数 makeSound(),展示多态性
for (const auto& animal : animals) {
animal->makeSound();
}
// 释放动态分配的内存
for (auto& animal : animals) {
delete animal;
}
animals.clear();
return 0;
}
说明
- 基类 Animal:
- 定义了一个纯虚函数 makeSound(),这使得 Animal 成为一个抽象类,不能直接实例化。
- 定义了虚析构函数,确保派生类对象被正确销毁。
- 派生类 Dog 和 Cat:
- 这两个类都继承了 Animal,并实现了各自的 makeSound() 函数。
- 多态性的使用:
- 创建一个 Animal 指针的向量 animals。
- 向向量中添加 Dog 和 Cat 对象。注意这里是通过基类指针来存储派生类对象,这是实现多态性的关键。
- 遍历向量并调用 makeSound() 函数。由于 makeSound() 是虚函数,调用时会根据实际对象的类型调用对应的函数,这就是多态性的体现。
- 最后,释放动态分配的内存,防止内存泄漏。
通过这种方式,你可以使用基类指针或引用来操作不同类型的派生类对象,并通过虚函数来实现不同的行为。这就是C++中多态性的强大之处。
初始化用法 :value(0)
在C++中,当使用new运算符创建对象时,你可以在创建对象时初始化其成员变量。这可以通过在new表达式后面使用括号提供构造函数的参数来实现。如果类有默认构造函数且你没有提供任何参数,那么括号可以省略。
以下是一些示例,展示了如何使用new运算符来创建对象并初始化成员变量:
示例 1:使用默认构造函数
假设有一个简单的类MyClass:
cppCopy Code
class MyClass {
public:
int value;
MyClass() : value(0) {
// 默认构造函数,将value初始化为0
}
};
int main() {
// 使用new运算符创建对象,调用默认构造函数
MyClass* obj = new MyClass;
// 访问成员变量
std::cout << "Value: " << obj->value << std::endl;
// 释放内存
delete obj;
return 0;
}
示例 2:使用带参数的构造函数
假设MyClass有一个带参数的构造函数:
cppCopy Code
class MyClass {
public:
int value;
MyClass(int val) : value(val) {
// 带参数的构造函数,将value初始化为传入的参数
}
};
int main() {
// 使用new运算符创建对象,并传递参数给构造函数
MyClass* obj = new MyClass(42);
// 访问成员变量
std::cout << "Value: " << obj->value << std::endl;
// 释放内存
delete obj;
return 0;
}
示例 3:初始化多个成员变量
假设MyClass有多个成员变量,并且你希望初始化它们:
cppCopy Code
class MyClass {
public:
int value1;
double value2;
MyClass(int val1, double val2) : value1(val1), value2(val2) {
// 带参数的构造函数,初始化多个成员变量
}
};
int main() {
// 使用new运算符创建对象,并传递多个参数给构造函数
MyClass* obj = new MyClass(42, 3.14);
// 访问成员变量
std::cout << "Value1: " << obj->value1 << ", Value2: " << obj->value2 << std::endl;
// 释放内存
delete obj;
return 0;
}
示例 4:初始化列表(C++11及以上)
在C++11及更高版本中,你还可以使用初始化列表来初始化对象:
cppCopy Code
class MyClass {
public:
int value1;
double value2;
// 可以省略构造函数,使用默认的初始化列表
};
int main() {
// 使用初始化列表创建对象
MyClass* obj = new MyClass{42, 3.14};
// 访问成员变量
std::cout << "Value1: " << obj->value1 << ", Value2: " << obj->value2 << std::endl;
// 释放内存
delete obj;
return 0;
}
总结
- 使用new运算符创建对象时,可以通过构造函数初始化成员变量。
- 如果类有默认构造函数,可以省略参数括号。
- 可以使用带参数的构造函数和初始化列表来初始化成员变量。
- 不要忘记在main函数结束前使用delete运算符释放动态分配的内存,以防止内存泄漏。
c++ dynamic_cast 用法
dynamic_cast 是 C++ 中用于在运行时安全地进行多态类型转换的操作符。它特别有用在涉及继承层次结构的指针或引用之间转换的场景。dynamic_cast 可以保证如果转换是非法的,它将返回 nullptr(对于指针)或抛出 std::bad_cast 异常(对于引用)。
以下是 dynamic_cast 的基本用法:
1. 用于指针
当你试图将一个基类指针转换为派生类指针时,可以使用 dynamic_cast。如果转换是有效的,dynamic_cast 返回指向派生类的指针;如果转换无效,它返回 nullptr。
cppCopy Code
#include <iostream>
#include <typeinfo>
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {
void derivedMethod() {}
};
int main() {
Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr) {
std::cout << "Conversion successful." << std::endl;
// 可以安全地调用 Derived 类的方法
derivedPtr->derivedMethod();
} else {
std::cout << "Conversion failed." << std::endl;
}
delete basePtr;
return 0;
}
2. 用于引用
dynamic_cast 也可以用于引用,但是如果转换失败,它会抛出 std::bad_cast 异常,因此需要使用 try-catch 块来捕获异常。
cppCopy Code
#include <iostream>
#include <typeinfo>
#include <stdexcept>
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {
public:
void derivedMethod() {}
};
int main() {
Base& baseRef = *new Derived();
try {
Derived& derivedRef = dynamic_cast<Derived&>(baseRef);
std::cout << "Conversion successful." << std::endl;
// 可以安全地调用 Derived 类的方法
derivedRef.derivedMethod();
} catch (const std::bad_cast& e) {
std::cout << "Conversion failed: " << e.what() << std::endl;
}
delete &baseRef;
return 0;
}
注意事项
- dynamic_cast 仅适用于含有虚函数的类(即多态类型)。如果类中不包含虚函数,使用 dynamic_cast 将导致编译错误。
- 相对于静态类型转换(如 static_cast),dynamic_cast 的开销更高,因为它在运行时检查类型的安全性。
- 当对指针进行 dynamic_cast 时,如果转换失败返回 nullptr,这要求程序员检查转换后的指针是否为空。
- 当对引用进行 dynamic_cast 时,如果转换失败则抛出异常,这要求使用异常处理机制来捕获并处理失败。
dynamic_cast 提供了在运行时安全地进行类型转换的方法,是处理 C++ 中复杂继承关系时的重要工具。
给我一个使用dynamic_cast的完整例子
当然,下面是一个使用 dynamic_cast 的完整例子,它展示了如何在 C++ 中利用 dynamic_cast 来实现运行时类型识别和安全的多态类型转换。
cppCopy Code
#include <iostream>
#include <typeinfo>
#include <exception>
// 基类
class Animal {
public:
virtual ~Animal() {} // 虚析构函数,确保派生类的析构函数能够被调用
virtual void speak() const = 0; // 纯虚函数,让Animal成为抽象类
};
// 派生类 Dog
class Dog : public Animal {
public:
void speak() const override {
std::cout << "Woof! Woof!" << std::endl;
}
void wagTail() const {
std::cout << "Wagging tail..." << std::endl;
}
};
// 派生类 Cat
class Cat : public Animal {
public:
void speak() const override {
std::cout << "Meow! Meow!" << std::endl;
}
void purr() const {
std::cout << "Purring..." << std::endl;
}
};
int main() {
// 创建一个指向Animal的指针,实际指向Dog对象
Animal* animalPtr = new Dog();
// 尝试将Animal指针转换为Dog指针
Dog* dogPtr = dynamic_cast<Dog*>(animalPtr);
if (dogPtr) {
std::cout << "Successfully cast to Dog." << std::endl;
dogPtr->speak(); // 输出:Woof! Woof!
dogPtr->wagTail(); // 输出:Wagging tail...
} else {
std::cout << "Failed to cast to Dog." << std::endl;
}
// 尝试将Animal指针转换为Cat指针
Cat* catPtr = dynamic_cast<Cat*>(animalPtr);
if (catPtr) {
std::cout << "Successfully cast to Cat." << std::endl;
catPtr->speak();
catPtr->purr();
} else {
std::cout << "Failed to cast to Cat." << std::endl; // 预期输出
}
// 清理内存
delete animalPtr;
// 为了展示对引用的dynamic_cast,我们创建一个Animal的引用,实际引用Dog对象
Animal& animalRef = *new Dog();
try {
// 尝试将Animal引用转换为Dog引用
Dog& dogRef = dynamic_cast<Dog&>(animalRef);
std::cout << "Successfully cast reference to Dog." << std::endl;
dogRef.speak(); // 输出:Woof! Woof!
dogRef.wagTail(); // 输出:Wagging tail...
} catch (const std::bad_cast& e) {
std::cout << "Failed to cast reference to Dog: " << e.what() << std::endl;
}
// 尝试将Animal引用转换为Cat引用(这将抛出异常)
try {
Cat& catRef = dynamic_cast<Cat&>(animalRef);
std::cout << "Successfully cast reference to Cat." << std::endl;
catRef.speak();
catRef.purr();
} catch (const std::bad_cast& e) {
std::cout << "Failed to cast reference to Cat: " << e.what() << std::endl; // 预期输出
}
// 清理内存
delete &animalRef;
return 0;
}
在这个例子中,我们定义了一个基类 Animal 和两个派生类 Dog 和 Cat。Animal 类有一个纯虚函数 speak,这使得 Animal 成为一个抽象类。Dog 和 Cat 类分别重写了 speak 方法,并添加了各自的特有方法(wagTail 和 purr)。
在 main 函数中,我们首先创建了一个指向 Dog 对象的 Animal 指针,并使用 dynamic_cast 尝试将其转换为 Dog 指针和 Cat 指针。转换到 Dog 指针成功,而转换到 Cat 指针失败(返回 nullptr)。
接下来,我们创建了一个引用 Dog 对象的 Animal 引用,并使用 dynamic_cast 尝试将其转换为 Dog 引用和 Cat 引用。转换到 Dog 引用成功,而转换到 Cat 引用则抛出了 std::bad_cast 异常。
注意,在实际应用中,通常不建议使用裸指针和手动内存管理(如 new 和 delete)。更好的做法是使用智能指针(如 std::unique_ptr 或 std::shared_ptr)来自动管理内存。然而,为了简化示例,这里我们仍然使用了裸指针。