C++ 以前学校的时候写过. 开始写代码了.
通过案例学习c++.
visual studio 下
面向对象的语言.怎么写一个对象
- 创建一个空项目
- c++ 一般情况下 头文件和源文件分开,所以 右键项目在文件资源管理器中打开文件夹,新建两个文件夹include,src.用于存放头文件和源文件
- 回到项目右键头文件->新建项->位置->选择刚才创建的include文件夹->新建 Dog.h(# pragme once 这个宏代表只引用一次, )
- 右键源文件->新建项->位置->选择刚才创建的src文件夹->新建Dog.cpp
- 右键项目 -> 属性 -> 常规 -> c/c++ 常规 -> 附加包的目录 -> 编辑 -> 新建 找到include文件夹. 应用 .
- Dog.h 新建头文件
- 总共三个文件 Dog.h头文件 Dog.cpp, cpp文件 main.cpp
例子1 构造函数 析构函数 命名空间
Dog.h
class Dog
{
public:
Dog();//构造函数 (malloc)
~Dog();//析构函数 释放在构造函数里面动态申请的内存 (free)
void setAge(int age);
int getAge();
void setName(char *name);
char* getName();
private:
int age;
char *name;
};
dog.cpp
#include <Dog.h>
#include <iostream>
//c++ 标准库的命名空间
using namespace std;
Dog::Dog(){
cout << "构造函数 Dog" << this << endl;
}
Dog::~Dog() {
cout << "析构函数 Dog" << this << endl;
}
void Dog::setAge(int age){
cout << "dog setAge age " << age << endl;
this ->age = age;
}
int Dog::getAge(){
return this->age;
}
void Dog::setName(char *name){
cout << "dog setName name " << name << endl;
this->name = name;
}
char* Dog::getName(){
return this->name;
}
main.cpp
#include <Dog.h>
#include <iostream>
using namespace std;
void fun(){
Dog dog;//局部变量
dog.setAge(5);
dog.setName("jumper");
cout << "dog age " << dog.getAge() << endl;
cout << "dog name " << dog.getName() << endl;
}
void main(){
fun();
system("pause");
}
运行结果:
分析下:
创建了一个Dog类 给Dog 设置了年龄和名字 ,设置的时候打印一次,然后在主函数里面调用再打印一次年龄和名字. 并且打印了构造函数和析构函数的地址 .
为什么会调用析构函数?
因为Dog 声明的时候是局部变量, 调用结束后会回收就会调用析构函数.
-
Dog(); 构造函数 用于初始化一些东西
~Dog(); 析构函数 对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。 -
Dog::Dog() 代表的意思 是Dog()这个方法 是Dog这个类里面的 .作用域限定符,当在类体中直接定义函数时,不需要在函数名字的前面加上类名,但是在类体外实现函数定义的时候,必须加上类名并且加上作用域限定符。Student::Display();
-
命名空间 //c++ 标准库的命名空间
using namespace std;
如果不加命名空间打印
std::cout << "构造函数 Dog" << std::endl;
如果加了命名空间
cout << "析构函数 Dog" << endl;
- << 是输出流
自定义命名空间
- 创建自己的命名空间 有点类似java的包 可以区分是自己的类还是其他人创建的类.
namespace NSP_A {
struct MyStudent
{
int age;
};
}
//使用
NSP_A::MyStudent t;
t.age = 18;
using NSP_A::MyStudent;
MyStudent t2;
t2.age = 16;
===
例子2 值传递 引用传递.
这个东西 我以前搞过. 比较熟悉.
说白了 写个方法把对象传过去后改变值, 并不会实际改变原始的值,只有传递引用过去才会改变原始的值.
值传递
#include <Dog.h>
#include <iostream>
using namespace std;
void fun(){
Dog dog;//局部变量
dog.setAge(5);
dog.setName("jumper");
cout << "dog age " << dog.getAge() << endl;
cout << "dog name " << dog.getName() << endl;
}
void setFunValue(Dog dog){
dog.setAge(10);
dog.setName("小白");
cout << "setFunValue age " << dog.getAge() << endl;
cout << "setFunValue name " << dog.getName() << endl;
}
void main(){
//fun();
Dog dog;
dog.setAge(2);
dog.setName("小黑");
cout << "main dog age " << dog.getAge() << endl;
cout << "main dog name " << dog.getName() << endl;
setFunValue(dog);
cout << "main dog age " << dog.getAge() << endl;
cout << "main dog name " << dog.getName() << endl;
system("pause");
}
结果:
可以看到
构造函数 Dog00FFFD78
dog setAge age 2
dog setName name 小黑
main dog age 2
main dog name 小黑
dog setAge age 10
dog setName name 小白
setFunValue age 10
setFunValue name 小白
析构函数 Dog00FFFC9C
main dog age 2
main dog name 小黑
请按任意键继续. . .
并没有改变 因为传递的是值 被回收了
传递引用
在上面的基础上加一个地址符 &
#include <Dog.h>
#include <iostream>
using namespace std;
void fun(){
Dog dog;//局部变量
dog.setAge(5);
dog.setName("jumper");
cout << "dog age " << dog.getAge() << endl;
cout << "dog name " << dog.getName() << endl;
}
//void setFunValue(Dog dog){
// dog.setAge(10);
// dog.setName("小白");
// cout << "setFunValue age " << dog.getAge() << endl;
// cout << "setFunValue name " << dog.getName() << endl;
//
//}
void setFunValue(Dog &dog){
dog.setAge(10);
dog.setName("小白");
cout << "setFunValue age " << dog.getAge() << endl;
cout << "setFunValue name " << dog.getName() << endl;
}
void main(){
//fun();
Dog dog;
dog.setAge(2);
dog.setName("小黑");
cout << "main dog age " << dog.getAge() << endl;
cout << "main dog name " << dog.getName() << endl;
setFunValue(dog);
cout << "main dog age " << dog.getAge() << endl;
cout << "main dog name " << dog.getName() << endl;
system("pause");
}
结果:
解析:
构造函数 Dog00AFF760
dog setAge age 2
dog setName name 小黑
main dog age 2
main dog name 小黑
dog setAge age 10
dog setName name 小白
setFunValue age 10
setFunValue name 小白
main dog age 10
main dog name 小白
请按任意键继续. . .
可以看到修改成功 因为使用了引用
例子3 给属性默认赋值
Dog.cpp 中
#include <Dog.h>
#include <iostream>
//c++ 标准库的命名空间
using namespace std;
Dog::Dog():name("旺财"),age(3){
cout << "构造函数 Dog" << this << endl;
}
Dog::~Dog() {
cout << "析构函数 Dog" << this << endl;
}
void Dog::setAge(int age){
cout << "dog setAge age " << age << endl;
this ->age = age;
}
int Dog::getAge(){
return this->age;
}
void Dog::setName(char *name){
cout << "dog setName name " << name << endl;
this->name = name;
}
char* Dog::getName(){
return this->name;
}
打印结果
构造函数 Dog008FF9E4
main dog age 3
main dog name 旺财
dog setAge age 10
dog setName name 小白
setFunValue age 10
setFunValue name 小白
main dog age 10
main dog name 小白
请按任意键继续. . .
可以看到 默认就给附上初始值了,实际上在main 中调用 Dog dog 的时候就初始化了 ,因为调用构造函数了.
例子4 带参数的构造函数, 对象和指针
Dog.h 头文件:
class Dog
{
public:
//Dog();//构造函数 (malloc)
~Dog();//析构函数 释放在构造函数里面动态申请的内存 (free)
Dog(int age, char *name);//带参数的构造函数
void setAge(int age);
int getAge();
void setName(char *name);
char* getName();
private:
int age;
char *name;
};
Dog.cpp文件
#include <Dog.h>
#include <iostream>
//c++ 标准库的命名空间
using namespace std;
//Dog::Dog():name("旺财"),age(3){
// cout << "构造函数 Dog" << this << endl;
//}
//这里构造函数 参数传递进来 进行赋值
Dog::Dog(int age, char *name) :age(age), name(name){
cout << "带参数的构造函数 Dog" << this << endl;
}
Dog::~Dog() {
cout << "析构函数 Dog" << this << endl;
}
void Dog::setAge(int age){
cout << "dog setAge age " << age << endl;
this ->age = age;
}
int Dog::getAge(){
return this->age;
}
void Dog::setName(char *name){
cout << "dog setName name " << name << endl;
this->name = name;
}
char* Dog::getName(){
return this->name;
}
main.cpp文件
#include <Dog.h>
#include <iostream>
using namespace std;
void fun(){
//Dog dog;//局部变量
Dog dog = Dog(2, "小黑");
//dog.setAge(5);
//dog.setName("jumper");
cout << "dog age " << dog.getAge() << endl;
cout << "dog name " << dog.getName() << endl;
}
void setFunValue(Dog &dog){
dog.setAge(10);
dog.setName("小白");
cout << "setFunValue age " << dog.getAge() << endl;
cout << "setFunValue name " << dog.getName() << endl;
}
//构造函数传参
void main(){
fun();
system("pause");
}
使用指针的情况
void fun(){
//Dog dog;//局部变量
//Dog dog = Dog(2, "小黑");
Dog *dog = new Dog(2, "小黑");
//dog.setAge(5);
//dog.setName("jumper");
cout << "dog age " << dog->getAge() << endl;
cout << "dog name " << dog->getName() << endl;
delete dog;
dog = NULL;
}
Dog *dog 代表指针. 访问的时候用 -> 访问.
使用指针和使用对象
没有new 关键字和有new 关键字 的异同.
有new 关键字的是指针 没有new关键字的是对象.
对象: 动态分配内存, 自动回收
指针: 自已申请内存, 需要自己回收
delete dog;
dog = NULL;
如果不自己回收就是野指针, 不会调用析构函数.
结果:
带参数的构造函数 Dog004FF9D8
dog age 2
dog name 小黑
析构函数 Dog004FF9D8
请按任意键继续. . .
例子5 拷贝构造函数 深拷贝浅拷贝
在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
简单的来说,浅拷贝是增加了一个指针,指向原来已经存在的内存。而深拷贝是增加了一个指针,并新开辟了一块空间,让指针指向这块新开辟的空间。 浅拷贝在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误
看这里
https://blog.csdn.net/qq_27011361/article/details/79518057