Home > Blockchain >  Can't understand the task of given function
Can't understand the task of given function

Time:12-08

New to Cpp. In the given code, I don't know what change_val(int k) = 0; means and why the compiler prints

    error: cannot declare variable 'ob1' to be of abstract type 'B'
      B ob1(10);

    error: invalid new-expression of abstract class type 'B'
     ob2 = new B(100);

To my knowledge, neither B nor A has been declared abstract. Then why can't I declare a variable of B object? And is it possible to assign object of one class to another object type as in A *ob2; ob2 = new B(100);?


The code:

#include<iostream>
using namespace std;
class A{
    int x;
public:
    A(){x=0;}
    explicit A(int x){ cout<<"Constructor of A called"<<endl;
        this->x = x 100;}
    virtual void change_val(int k) = 0;
    void set_x(int k){x = k;}
    int get_val() const{ return x; }
    virtual void print_value()
    {
        cout<<x<<endl;
    }
};

class B : public A{
    public:
            explicit B(int k):A(k){
                cout<<"Constructor of B called"<<endl;
            }
    void print_value() override
        {
                cout<< get_val() 200<<endl;
        }
};

int main(){
    B ob1(10);
    ob1.print_value ();
    A *ob2;
    ob2 = new B(100);
    ob2->print_value ();
    ob2->change_val(20);
    ob2->print_value ();
}

CodePudding user response:

We'll start with a quick review of your code:

#include<iostream>
using namespace std;  // Bad practice

class A{
    int x;
public:
    A(){x=0;}  // Bad practice; utilize default member initialization
    explicit A(int x){ cout<<"Constructor of A called"<<endl;  // Bad practice; utilize initialization section
        this->x = x 100;}  // Poor style throughout this code
    // Missing virtual destructor
    virtual void change_val(int k) = 0;  // Pure virtual function, makes A an abstract class
    void set_x(int k){x = k;}
    int get_val() const{ return x; }
    virtual void print_value()
    {
        cout<<x<<endl;  // Prefer '\n' over std::endl
    }
};

class B : public A{
    public:
            explicit B(int k):A(k){  // Why use initialization here but not above?
                cout<<"Constructor of B called"<<endl;
            }
    void print_value() override
        {
                cout<< get_val() 200<<endl;
        }

    // Failed to override pure virtual function change_val(), making B abstract as well
};

int main(){
    B ob1(10);
    ob1.print_value ();
    A *ob2;
    ob2 = new B(100);  // Initialize on same line as declaration
    ob2->print_value ();
    ob2->change_val(20);
    ob2->print_value ();
    // Missing delete
}

This is subjective, so I didn't note it above, but it's also possible for the member x to be protected in A so that B can directly access it instead of going through a getter. My code below does this. I see no reason to make implementing children classes more difficult.

#include <iostream>
// using namespace std;  // Bad practice

class A {
 protected:
  int x = 0;

 public:
  A() = default;
  virtual ~A() = default;
  explicit A(int x) : x(x   100) { std::cout << "A ctor" << '\n'; }

  // Pure virtual function, makes A abstract
  virtual void change_val(int k) = 0;
  void set_x(int k) { x = k; }
  int get_val() const { return x; }
  virtual void print_value() { std::cout << x << '\n'; }
};

class B : public A {
 public:
  explicit B(int k) : A(k) { std::cout << "B ctor" << '\n'; }

  // Simpler function due to x being protected now in A
  void print_value() override { std::cout << x   200 << '\n'; }

  // Overriding the pure virtual function makes B a concrete class now
  void change_val(int k) override { x = k; }
};

int main() {
  B ob1(10);
  ob1.print_value();
  A *ob2 = new B(100);
  ob2->print_value();
  ob2->change_val(20);
  ob2->print_value();

  delete ob2;
}

Output:

❯ ./a.out 
A ctor
B ctor
310
A ctor
B ctor
400
220

I noted that a virtual destructor was missing. It's important to have this for all base classes so that your derived objects can be properly destroyed.

You also called new without a subsequent delete. In bigger programs doing actual work, this would have leaked memory. To avoid this issue, you could use std::unique_ptr from <memory>. It handles the creation and destruction for you.

#include <memory>

/*
 * Your code
 */

int main() {
  B ob1(10);
  ob1.print_value();
  std::unique_ptr<A> ob2(new B(100));
  ob2->print_value();
  ob2->change_val(20);
  ob2->print_value();
}

CodePudding user response:

To my knowledge, neither B nor A has been declared abstract.

In c there is no abstract keyword (like in Java, for example) or declaration. The compiler decides for you whether the class is abstract using its definition. In c the class is abstract, if it contains one or more pure virtual methods (that = 0; thing after method signature).

So, A is implicitly declared as abstract (it has pure virtual method change_val()). B extends A and overrides the print_value() method. However, it does not implement the change_val() method, so it is implicitly declared as abstract too.

So it is easy to understand the errors your compiler generated.

    error: cannot declare variable 'ob1' to be of abstract type 'B'
      B ob1(10);

That happens because you try to instantiate an instance of abstract class B. It is impossible in other languages too.

The explanation for the second error is the same.

    error: invalid new-expression of abstract class type 'B'
     ob2 = new B(100);

You are trying to instantiate B which is an abstract type (sometimes called incomplete type).

And is it possible to assign object of one class to another object type as in A *ob2; ob2 = new B(100);?

If you create class C which inherits B and implements the change_val() method, so the class is not abstract anymore, it is possible to say for example:

class A{
    int x;
public:
    A(){
        x=0;
    }

    explicit A(int x) {
        cout << "Constructor of A called" << endl;
        this->x = x   100;
    }

    virtual void change_val(int k) = 0;

    void set_x(int k) {
        x = k;
    }

    int get_val() const {
        return x; 
    }

    virtual void print_value() {
        cout << x << endl;
    }
};

class B : public A {
public:
    explicit B(int k): A(k) {
        cout << "Constructor of B called" << endl;
    }

    void print_value() override {
        cout << get_val()   200 << endl;
    }
};

class C : public B {
public:
    explicit C(int number) : B(number) {
        cout << "Constructor of C called" << endl;
    }

    void change_val(int number) override {
        set_x(number);
    }
};

int main() {
    A* obj = new C(100);

    obj->change_val(200);
    obj->print_value();
}

The following program will output 400.

  • Related