C++ 对象创建
5. Constructor(构造函数)
- If a class has a constructor, the compiler automatically calls that constructor at the point an object is created, before client programmers can get their hands on the object.
- The name of the constructor is the same as the name of the class.
5.1 Destructor(析构函数)
-
In C++, cleanup is as important as initialization and is therefore guaranteed with the destructor.
-
The destructor is named after the name of the class with a leading tilde(~). The destructor never has any arguments.
-
The destructor is called automatically by the compiler when the object goes out of scope.
-
constructor.cpp
#include <stdio.h>
class A
{
public:
int i;
A();
~A();
void f();
};
// 构造函数
A::A()
{
i = 10;
printf("A::A() -- this = %p\n", this);
}
// 析构函数
A::~A()
{
printf("A::~A() destructor...");
}
void A::f()
{
i = 20;
printf("A::f() -- &i = %p\n", &i);
printf("A::f() -- this = %p\n", this);
}
int main()
{
A a;
a.f();
return 0;
}
- 结果输出:
6. 对象初始化
6.1 内存分配
- The compiler allocates all the storage for a scope at the opening brace of that scope.
- The constructor call doesn't happen until the sequence point where the object is defined.
#include <stdio.h>
class A
{
public:
// 对变量 i 的声明
int i;
A();
void f();
};
A::A()
{
printf("A::A() -- this = %p\n", this);
}
void A::f()
{
i = 20;
// 输出 i 的地址
printf("A::f() -- &i = %p\n", &i);
printf("A::f() -- this = %p\n", this);
}
int main()
{
A a;
a.i = 10;
a.f();
// 定义 aa 的时候,才会调用构造函数
A aa;
aa.f();
return 0;
}
-
结果输出:
-
内存分配问题:
nojump.cpp
class X {
public:
X();
};
X::X() {}
void f(int i) {
if (i < 10) {
goto jump1;
}
X x1;
jump1:
switch(i) {
case 1:
X x2;
break;
case 2:
X x3;
break;
}
}
int main()
{
f(9);
f(11);
}
6.2 默认构造函数
- A default constructor is one that can be called with no arguments.
struct Y {
float f;
int i;
Y(int a);
};
- 对象初始化
// 正确:
Y y1[] = {Y(1), Y(2), Y(3)};
// 错误:
Y y2[2] = {Y(1)};
a.cpp
#include <stdio.h>
class A
{
public:
// 对变量 i 的声明
int i;
A(int a);
};
A::A(int a)
{
i = a;
printf("A::A() -- this = %p\n", this);
}
int main()
{
// 编译出错,由于没有默认构造函数
A y[2] = {A(1)};
return 0;
}
6.3 动态分配内存
- new is the way to allocate memory as a program runs. Pointers become the only access to that memory.
- delete enables you to return memory to the memory pool when you are finished with it.
// new
new int;
new Stash;
new int[10];
// delete
delete p;
delete[] p;
// 示例
int *psome = new int[10];
// The new operator returns the address of the first element of the block.
delete[] psome;
// The presence of the brackets tells the program that it should free the whole array, not just the element.
new_delete.cpp
#include <iostream>
using namespace std;
class A
{
private:
int i;
public:
A() {
i = 0;
cout << "A::A() running~" << endl;
}
~A(){
cout << "A::~A(), i = " << i << endl;
}
void set(int i) {
this->i = i;
}
void f() {
cout << "f() running~" << endl;
}
};
int main()
{
A* p = new A[10];
for (int i = 0; i < 10; i++) {
p[i].set(i);
}
// 此时,只会调用一次析构函数
// delete p;
// 此时,会调用 10 次析构函数
delete[] p;
return 0;
}
6.3.1 new 和 delete 建议
- Don't use delete to free memory that new didn't allocate.
- Don't use delete to free the same block of memory twice in succession.
- Use delete[] if you used new [] to allocate an array.
- Use delete(no brackets) if you used new to allocate a single entity.
- It's safe to apply delete to the null pointer(nothing happens).
// 删除空指针
int main()
{
int *p;
delete p;
return 0;
}
6.4 访问限制(private, public)
- To keep the client programmer's hands off members they shouldn't touch.
- To allow the library designer to change the internal workings of the structure without worrying about how it will affect the client programmer.
- The members of a class can be cataloged(分类), marked as:
- public
- private
- protected
6.4.1 public
- public means all member declarations that follow are available to everyone.
6.4.2 private
- The private keyword means that no one can access that member except inside function members of that type.
-d.cpp
#include <iostream>
using namespace std;
class A {
private:
int i;
int *p;
public:
A() { p = 0; cout << "A::A()" << endl;}
~A(){ if (p) delete p; cout << "A::~A()" << i << endl;}
void set(int ii) { i = ii; }
void f() { p = new int; }
// 指针可以获取到 私有的 i
// private 是针对类,而不是针对对象
// private 的限制,仅仅在编译时刻。
// 同一个类的对象之间,是可以互相访问私有的成员变量
void g(A* q) { cout << "A::g(), q->i = " << q->i << endl; }
};
int main()
{
A* p = new A[10];
for (int i = 0; i < 10; i++) {
p[i].set(i);
}
A b;
b.set(100);
p[0].g(&b);
delete[] p;
return 0;
}
6.4.3 friends
-
To explicitly grant access to a function that isn't a member of the structure.
-
The class itself controls which code has access to its members.
-
Can declare a global function as a friend, as well as a member function of another class, or even an entire class, as a friend.
-
my_friend.cpp
// declaration (incomplete type specification)
struct X;
struct Y {
void f(X*);
};
// definition
struct X {
private:
int i;
public:
void initialize();
// global friend
friend void g(X*, int);
// struct member friend
friend void Y::f(X*);
// Entire struct is a friend
friend struct Z;
friend void h();
};
void X::initialize() {
i = 0;
}
void g(X* x, int i) {
x->i = i;
}
void Y::f(X* x) {
x->i = 47;
}
struct Z {
private:
int j;
public:
void m();
};
int main()
{
return 0;
}
6.4.4 class vs. struct
- class defaults to private;
- struct defaults to public.
6.5 初始化列表(initializer list)
- Can initialize any type of data
- pseudo-constructor calls for built-ins.
- No need to perform assignment within body of constructor.
- Order of initialization is order of declaration
- Not the order in the list!
- Destroyed in the reverse order.
class Point {
private:
const float x, y;
// 使用初始化列表,对 x, y 初始化
Point(float xa = 0.0, float ya = 0.0) : y(ya), x(xa) {}
};
6.5.1 Initialization vs. assignment
// 示例一:仅做初始化
Student::Student(string s):name(s) {}
// initialization
// before constructor
// 示例二:先做初始化,再做赋值
Student::Student(string s) {name = s;}
// assignment
// inside constructor
// 由于先做初始化,name 必须提供一个默认构造函数
// 示例三:
#include <iostream>
using namespace std;
class B {
public:
B(int i) {}
};
struct A {
private:
int i;
int *p;
B b;
public:
A():p(0) {
// 此处,对 b 赋值之前,会使用默认构造函数,初始化 b
// 由于没有默认构造函数 B::B(),从而无法初始化,导致编译失败
b = 0;
cout << "A::A()" << endl;
}
void set(int ii) { i = ii; }
};
int main()
{
A* p = new A[10];
for (int i = 0; i < 10; i++) {
p[i].set(i);
}
return 0;
}
- 编译异常截图
参考资料: