diff --git a/gdb/ctf.c b/gdb/ctf.c
index 07ae93b999..b09cebbf07 100644
--- a/gdb/ctf.c
+++ b/gdb/ctf.c
@@ -32,6 +32,40 @@
 #include <ctype.h>
 #include <algorithm>
 
+/* The CTF target.  */
+
+class ctf_target final : public tracefile_target
+{
+public:
+  const char *shortname () override
+  { return "ctf"; }
+
+  const char *longname () override
+  { return _("CTF file"); }
+
+  const char *doc () override
+  {
+    return _("\
+Use a CTF directory as a target.\n\
+Specify the filename of the CTF directory.");
+  }
+
+  void open (const char *, int) override;
+  void close () override;
+  void fetch_registers (struct regcache *, int) override;
+  enum target_xfer_status xfer_partial (enum target_object object,
+						const char *annex,
+						gdb_byte *readbuf,
+						const gdb_byte *writebuf,
+						ULONGEST offset, ULONGEST len,
+						ULONGEST *xfered_len) override;
+  void files_info () override;
+  int trace_find (enum trace_find_type type, int num,
+			  CORE_ADDR addr1, CORE_ADDR addr2, int *tpp) override;
+  int get_trace_state_variable_value (int tsv, LONGEST *val) override;
+  traceframe_info_up traceframe_info () override;
+};
+
 /* GDB saves trace buffers and other information (such as trace
    status) got from the remote target into Common Trace Format (CTF).
    The following types of information are expected to save in CTF:
@@ -826,7 +860,7 @@ static struct bt_iter_pos *start_pos;
 /* The name of CTF directory.  */
 static char *trace_dirname;
 
-static struct target_ops ctf_ops;
+static ctf_target ctf_ops;
 
 /* Destroy ctf iterator and context.  */
 
@@ -1074,8 +1108,8 @@ ctf_read_tp (struct uploaded_tp **uploaded_tps)
    definitions from the first packet.  Set the start position at the
    second packet which contains events on trace blocks.  */
 
-static void
-ctf_open (const char *dirname, int from_tty)
+void
+ctf_target::open (const char *dirname, int from_tty)
 {
   struct bt_ctf_event *event;
   uint32_t event_id;
@@ -1142,8 +1176,8 @@ ctf_open (const char *dirname, int from_tty)
 /* This is the implementation of target_ops method to_close.  Destroy
    CTF iterator and context.  */
 
-static void
-ctf_close (struct target_ops *self)
+void
+ctf_target::close ()
 {
   int pid;
 
@@ -1161,8 +1195,8 @@ ctf_close (struct target_ops *self)
 /* This is the implementation of target_ops method to_files_info.
    Print the directory name of CTF trace data.  */
 
-static void
-ctf_files_info (struct target_ops *t)
+void
+ctf_target::files_info ()
 {
   printf_filtered ("\t`%s'\n", trace_dirname);
 }
@@ -1172,9 +1206,8 @@ ctf_files_info (struct target_ops *t)
    extract contents from events, and set REGCACHE with the contents.
    If no matched events are found, mark registers unavailable.  */
 
-static void
-ctf_fetch_registers (struct target_ops *ops,
-		     struct regcache *regcache, int regno)
+void
+ctf_target::fetch_registers (struct regcache *regcache, int regno)
 {
   struct gdbarch *gdbarch = regcache->arch ();
   struct bt_ctf_event *event = NULL;
@@ -1258,11 +1291,11 @@ ctf_fetch_registers (struct target_ops *ops,
    OFFSET is within the range, read the contents from events to
    READBUF.  */
 
-static enum target_xfer_status
-ctf_xfer_partial (struct target_ops *ops, enum target_object object,
-		  const char *annex, gdb_byte *readbuf,
-		  const gdb_byte *writebuf, ULONGEST offset,
-		  ULONGEST len, ULONGEST *xfered_len)
+enum target_xfer_status
+ctf_target::xfer_partial (enum target_object object,
+			  const char *annex, gdb_byte *readbuf,
+			  const gdb_byte *writebuf, ULONGEST offset,
+			  ULONGEST len, ULONGEST *xfered_len)
 {
   /* We're only doing regular memory for now.  */
   if (object != TARGET_OBJECT_MEMORY)
@@ -1397,9 +1430,8 @@ ctf_xfer_partial (struct target_ops *ops, enum target_object object,
    trace variable is found, set the value of it to *VAL and return
    true, otherwise return false.  */
 
-static int
-ctf_get_trace_state_variable_value (struct target_ops *self,
-				    int tsvnum, LONGEST *val)
+int
+ctf_target::get_trace_state_variable_value (int tsvnum, LONGEST *val)
 {
   struct bt_iter_pos *pos;
   int found = 0;
@@ -1515,9 +1547,9 @@ ctf_get_traceframe_address (void)
    Iterate the events whose name is "frame", extract the tracepoint
    number in it.  Return traceframe number when matched.  */
 
-static int
-ctf_trace_find (struct target_ops *self, enum trace_find_type type, int num,
-		CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
+int
+ctf_target::trace_find (enum trace_find_type type, int num,
+			CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
 {
   int tfnum = 0;
   int found = 0;
@@ -1617,10 +1649,10 @@ ctf_trace_find (struct target_ops *self, enum trace_find_type type, int num,
    frame, extract memory range information, and return them in
    traceframe_info.  */
 
-static traceframe_info_up
-ctf_traceframe_info (struct target_ops *self)
+traceframe_info_up
+ctf_target::traceframe_info ()
 {
-  traceframe_info_up info (new traceframe_info);
+  traceframe_info_up info (new struct traceframe_info);
   const char *name;
   struct bt_iter_pos *pos;
 
@@ -1684,27 +1716,6 @@ ctf_traceframe_info (struct target_ops *self)
   return info;
 }
 
-static void
-init_ctf_ops (void)
-{
-  memset (&ctf_ops, 0, sizeof (ctf_ops));
-
-  init_tracefile_ops (&ctf_ops);
-  ctf_ops.to_shortname = "ctf";
-  ctf_ops.to_longname = "CTF file";
-  ctf_ops.to_doc = "Use a CTF directory as a target.\n\
-Specify the filename of the CTF directory.";
-  ctf_ops.to_open = ctf_open;
-  ctf_ops.to_close = ctf_close;
-  ctf_ops.to_fetch_registers = ctf_fetch_registers;
-  ctf_ops.to_xfer_partial = ctf_xfer_partial;
-  ctf_ops.to_files_info = ctf_files_info;
-  ctf_ops.to_trace_find = ctf_trace_find;
-  ctf_ops.to_get_trace_state_variable_value
-    = ctf_get_trace_state_variable_value;
-  ctf_ops.to_traceframe_info = ctf_traceframe_info;
-}
-
 #endif
 
 /* module initialization */
@@ -1713,8 +1724,6 @@ void
 _initialize_ctf (void)
 {
 #if HAVE_LIBBABELTRACE
-  init_ctf_ops ();
-
   add_target_with_completer (&ctf_ops, filename_completer);
 #endif
 }
diff --git a/gdb/tracefile-tfile.c b/gdb/tracefile-tfile.c
index d7e934770a..af8e3bd83b 100644
--- a/gdb/tracefile-tfile.c
+++ b/gdb/tracefile-tfile.c
@@ -38,6 +38,42 @@
 #define O_LARGEFILE 0
 #endif
 
+/* The tfile target.  */
+
+class tfile_target final : public tracefile_target
+{
+ public:
+  const char *shortname () override
+  { return "tfile"; }
+
+  const char *longname () override
+  { return _("Local trace dump file"); }
+
+  const char *doc () override
+  {
+    return _("\
+Use a trace file as a target.  Specify the filename of the trace file.");
+  }
+
+  void open (const char *, int) override;
+  void close () override;
+  void fetch_registers (struct regcache *, int) override;
+  enum target_xfer_status xfer_partial (enum target_object object,
+						const char *annex,
+						gdb_byte *readbuf,
+						const gdb_byte *writebuf,
+						ULONGEST offset, ULONGEST len,
+						ULONGEST *xfered_len) override;
+  void files_info () override;
+  int trace_find (enum trace_find_type type, int num,
+			  CORE_ADDR addr1, CORE_ADDR addr2, int *tpp) override;
+  int get_trace_state_variable_value (int tsv, LONGEST *val) override;
+  traceframe_info_up traceframe_info () override;
+
+  void get_tracepoint_status (struct breakpoint *tp,
+			      struct uploaded_tp *utp) override;
+};
+
 /* TFILE trace writer.  */
 
 struct tfile_trace_file_writer
@@ -275,7 +311,7 @@ tfile_write_tdesc (struct trace_file_writer *self)
     = (struct tfile_trace_file_writer *) self;
 
   gdb::optional<std::string> tdesc
-    = target_fetch_description_xml (&current_target);
+    = target_fetch_description_xml (target_stack);
 
   if (!tdesc)
     return;
@@ -378,9 +414,7 @@ tfile_trace_file_writer_new (void)
 
 /* target tfile command */
 
-static struct target_ops tfile_ops;
-
-/* Fill in tfile_ops with its defined operations and properties.  */
+static tfile_target tfile_ops;
 
 #define TRACE_HEADER_SIZE 8
 
@@ -417,8 +451,8 @@ tfile_read (gdb_byte *readbuf, int size)
     error (_("Premature end of file while reading trace file"));
 }
 
-static void
-tfile_open (const char *arg, int from_tty)
+void
+tfile_target::open (const char *arg, int from_tty)
 {
   int flags;
   int scratch_chan;
@@ -580,8 +614,8 @@ tfile_interp_line (char *line, struct uploaded_tp **utpp,
 
 /* Close the trace file and generally clean up.  */
 
-static void
-tfile_close (struct target_ops *self)
+void
+tfile_target::close ()
 {
   int pid;
 
@@ -592,7 +626,7 @@ tfile_close (struct target_ops *self)
   inferior_ptid = null_ptid;	/* Avoid confusion from thread stuff.  */
   exit_inferior_silent (pid);
 
-  close (trace_fd);
+  ::close (trace_fd);
   trace_fd = -1;
   xfree (trace_filename);
   trace_filename = NULL;
@@ -601,15 +635,14 @@ tfile_close (struct target_ops *self)
   trace_reset_local_state ();
 }
 
-static void
-tfile_files_info (struct target_ops *t)
+void
+tfile_target::files_info ()
 {
   printf_filtered ("\t`%s'\n", trace_filename);
 }
 
-static void
-tfile_get_tracepoint_status (struct target_ops *self,
-			     struct breakpoint *tp, struct uploaded_tp *utp)
+void
+tfile_target::get_tracepoint_status (struct breakpoint *tp, struct uploaded_tp *utp)
 {
   /* Other bits of trace status were collected as part of opening the
      trace files, so nothing to do here.  */
@@ -653,9 +686,9 @@ tfile_get_traceframe_address (off_t tframe_offset)
    both the traceframe and tracepoint number, otherwise -1 for
    each.  */
 
-static int
-tfile_trace_find (struct target_ops *self, enum trace_find_type type, int num,
-		  CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
+int
+tfile_target::trace_find (enum trace_find_type type, int num,
+			  CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
 {
   short tpnum;
   int tfnum = 0, found = 0;
@@ -836,9 +869,8 @@ traceframe_find_block_type (char type_wanted, int pos)
 /* Look for a block of saved registers in the traceframe, and get the
    requested register from it.  */
 
-static void
-tfile_fetch_registers (struct target_ops *ops,
-		       struct regcache *regcache, int regno)
+void
+tfile_target::fetch_registers (struct regcache *regcache, int regno)
 {
   struct gdbarch *gdbarch = regcache->arch ();
   int offset, regn, regsize, dummy;
@@ -883,7 +915,7 @@ tfile_fetch_registers (struct target_ops *ops,
 }
 
 static enum target_xfer_status
-tfile_xfer_partial_features (struct target_ops *ops, const char *annex,
+tfile_xfer_partial_features (const char *annex,
 			     gdb_byte *readbuf, const gdb_byte *writebuf,
 			     ULONGEST offset, ULONGEST len,
 			     ULONGEST *xfered_len)
@@ -909,15 +941,15 @@ tfile_xfer_partial_features (struct target_ops *ops, const char *annex,
   return TARGET_XFER_OK;
 }
 
-static enum target_xfer_status
-tfile_xfer_partial (struct target_ops *ops, enum target_object object,
-		    const char *annex, gdb_byte *readbuf,
-		    const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
-		    ULONGEST *xfered_len)
+enum target_xfer_status
+tfile_target::xfer_partial (enum target_object object,
+			    const char *annex, gdb_byte *readbuf,
+			    const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
+			    ULONGEST *xfered_len)
 {
   /* We're only doing regular memory and tdesc for now.  */
   if (object == TARGET_OBJECT_AVAILABLE_FEATURES)
-    return tfile_xfer_partial_features (ops, annex, readbuf, writebuf,
+    return tfile_xfer_partial_features (annex, readbuf, writebuf,
 					offset, len, xfered_len);
   if (object != TARGET_OBJECT_MEMORY)
     return TARGET_XFER_E_IO;
@@ -1001,9 +1033,8 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
 /* Iterate through the blocks of a trace frame, looking for a 'V'
    block with a matching tsv number.  */
 
-static int
-tfile_get_trace_state_variable_value (struct target_ops *self,
-				      int tsvnum, LONGEST *val)
+int
+tfile_target::get_trace_state_variable_value (int tsvnum, LONGEST *val)
 {
   int pos;
   int found = 0;
@@ -1085,10 +1116,10 @@ build_traceframe_info (char blocktype, void *data)
   return 0;
 }
 
-static traceframe_info_up
-tfile_traceframe_info (struct target_ops *self)
+traceframe_info_up
+tfile_target::traceframe_info ()
 {
-  traceframe_info_up info (new traceframe_info);
+  traceframe_info_up info (new struct traceframe_info);
 
   traceframe_walk_blocks (build_traceframe_info, 0, info.get ());
 
@@ -1105,31 +1136,8 @@ tfile_append_tdesc_line (const char *line)
   buffer_grow_str (&trace_tdesc, "\n");
 }
 
-static void
-init_tfile_ops (void)
-{
-  init_tracefile_ops (&tfile_ops);
-
-  tfile_ops.to_shortname = "tfile";
-  tfile_ops.to_longname = "Local trace dump file";
-  tfile_ops.to_doc
-    = "Use a trace file as a target.  Specify the filename of the trace file.";
-  tfile_ops.to_open = tfile_open;
-  tfile_ops.to_close = tfile_close;
-  tfile_ops.to_fetch_registers = tfile_fetch_registers;
-  tfile_ops.to_xfer_partial = tfile_xfer_partial;
-  tfile_ops.to_files_info = tfile_files_info;
-  tfile_ops.to_get_tracepoint_status = tfile_get_tracepoint_status;
-  tfile_ops.to_trace_find = tfile_trace_find;
-  tfile_ops.to_get_trace_state_variable_value
-    = tfile_get_trace_state_variable_value;
-  tfile_ops.to_traceframe_info = tfile_traceframe_info;
-}
-
 void
 _initialize_tracefile_tfile (void)
 {
-  init_tfile_ops ();
-
   add_target_with_completer (&tfile_ops, filename_completer);
 }
diff --git a/gdb/tracefile.c b/gdb/tracefile.c
index 58d620c3af..9c9c75398e 100644
--- a/gdb/tracefile.c
+++ b/gdb/tracefile.c
@@ -425,16 +425,16 @@ tracefile_fetch_registers (struct regcache *regcache, int regno)
 
 /* This is the implementation of target_ops method to_has_all_memory.  */
 
-static int
-tracefile_has_all_memory (struct target_ops *ops)
+int
+tracefile_target::has_all_memory ()
 {
   return 1;
 }
 
 /* This is the implementation of target_ops method to_has_memory.  */
 
-static int
-tracefile_has_memory (struct target_ops *ops)
+int
+tracefile_target::has_memory ()
 {
   return 1;
 }
@@ -443,8 +443,8 @@ tracefile_has_memory (struct target_ops *ops)
    The target has a stack when GDB has already selected one trace
    frame.  */
 
-static int
-tracefile_has_stack (struct target_ops *ops)
+int
+tracefile_target::has_stack ()
 {
   return get_traceframe_number () != -1;
 }
@@ -453,8 +453,8 @@ tracefile_has_stack (struct target_ops *ops)
    The target has registers when GDB has already selected one trace
    frame.  */
 
-static int
-tracefile_has_registers (struct target_ops *ops)
+int
+tracefile_target::has_registers ()
 {
   return get_traceframe_number () != -1;
 }
@@ -462,8 +462,8 @@ tracefile_has_registers (struct target_ops *ops)
 /* This is the implementation of target_ops method to_thread_alive.
    tracefile has one thread faked by GDB.  */
 
-static int
-tracefile_thread_alive (struct target_ops *ops, ptid_t ptid)
+int
+tracefile_target::thread_alive (ptid_t ptid)
 {
   return 1;
 }
@@ -471,8 +471,8 @@ tracefile_thread_alive (struct target_ops *ops, ptid_t ptid)
 /* This is the implementation of target_ops method to_get_trace_status.
    The trace status for a file is that tracing can never be run.  */
 
-static int
-tracefile_get_trace_status (struct target_ops *self, struct trace_status *ts)
+int
+tracefile_target::get_trace_status (struct trace_status *ts)
 {
   /* Other bits of trace status were collected as part of opening the
      trace files, so nothing to do here.  */
@@ -480,19 +480,9 @@ tracefile_get_trace_status (struct target_ops *self, struct trace_status *ts)
   return -1;
 }
 
-/* Initialize OPS for tracefile related targets.  */
-
-void
-init_tracefile_ops (struct target_ops *ops)
+tracefile_target::tracefile_target ()
 {
-  ops->to_stratum = process_stratum;
-  ops->to_get_trace_status = tracefile_get_trace_status;
-  ops->to_has_all_memory = tracefile_has_all_memory;
-  ops->to_has_memory = tracefile_has_memory;
-  ops->to_has_stack = tracefile_has_stack;
-  ops->to_has_registers = tracefile_has_registers;
-  ops->to_thread_alive = tracefile_thread_alive;
-  ops->to_magic = OPS_MAGIC;
+  this->to_stratum = process_stratum;
 }
 
 void
diff --git a/gdb/tracefile.h b/gdb/tracefile.h
index e6d4460d3e..accd038ebd 100644
--- a/gdb/tracefile.h
+++ b/gdb/tracefile.h
@@ -2,6 +2,7 @@
 #define TRACEFILE_H 1
 
 #include "tracepoint.h"
+#include "target.h"
 
 struct trace_file_writer;
 
@@ -113,7 +114,20 @@ struct trace_file_writer
 
 extern struct trace_file_writer *tfile_trace_file_writer_new (void);
 
-extern void init_tracefile_ops (struct target_ops *ops);
+/* Base class for tracefile related targets.  */
+
+class tracefile_target : public target_ops
+{
+public:
+  tracefile_target ();
+
+  int get_trace_status (trace_status *ts) override;
+  int has_all_memory () override;
+  int has_memory () override;
+  int has_stack () override;
+  int has_registers () override;
+  int thread_alive (ptid_t ptid) override;
+};
 
 extern void tracefile_fetch_registers (struct regcache *regcache, int regno);
 
